MIME Sniffing in Browsers and the Security Implications

Introduction

Whenever a website in opened in a browser, there are many tasks that are being silently performed in the background. One of those tasks is fetching resources such as images, stylesheets and JavaScript from different domains on the internet and then parsing those resources. For example, a browser fetches an image from remote server and renders it for display when it encounters an <img> tag with src attribute in an HTML document. Browsers handle these resources based on their MIME type, and a browser’s behavior can be guided by the X-Content-Type-Options HTTP header returned by the web server. In this post, we are going to look at security risks for an application that does not make use of this header. Specifically, we will look at the conditions under which exploitable vulnerabilities arise.

MIME Sniffing Introduction

MIME stands for “Multipurpose Internet Mail Extensions.” MIME was originally defined to support non-ASCII text and non-text binaries in email. However, the content types defined in MIME standard are used in HTTP protocol to define the type of content in a request or response. 

A browser usually identifies a resource’s MIME type by observing the Content-Type response header in an HTTP response. 

Figure 1:Content-Type response header for an HTML page from google.com

Sometimes, developers set values for Content-Type headers that are not appropriate for the response’s content. For example, if a server sends text/plain value for a JavaScript resource, it is a mismatch. As per web standards, text/plain is not a valid JavaScript MIME type. However, browsers may parse and render such misrepresented resources so that the website will operate as intended. This is where MIME sniffing comes into picture. An example has been given in Figure 2.  

Figure 2: A JavaScript resource served with incorrect Content-Type value

 “MIME sniffing” can be broadly defined as the practice adopted by browsers to determine the effective MIME type of a web resource by examining the content of the response instead of relying on the Content-Type header. MIME sniffing is performed only under specific conditions. Please note that MIME sniffing algorithms vary by browser. A MIME sniffing standard has been defined on the Web Hypertext Application Technology Working Group (WHATWG) website. 

A demo of MIME sniffing behavior of browsers can be run from a simple Docker container that can be set up from the GitHub repository:

https://github.com/denimgroup/denimgroup-vulnerability-examples

Follow the instructions in the GitHub README to download the container, build, and run it. Then open the web page in a browser of your choice where the traffic is run through a proxy such as OWASP ZAP, and observe the relevant HTTP response headers as well as the messages in the console.

X-Content-Type-Options HTTP Header

X-Content-Type-Options (XCTO) is a security-related HTTP response header used by servers to instruct browsers to not perform MIME sniffing. The only possible directive for this header is nosniff.  This header should be deployed by developers when they are sure that the MIME type in Content-Type header is appropriate for the response’s content. 

There are some additional advantages of using this header if the client is a Chromium based browser. We will see more details about this in a later section of this article. 

Cross-Site Scripting Using MIME Sniffing

The XCTO header is mainly useful in two parsing contexts:  JavaScript and CSS. This is because in these contexts, client-side code execution is possible.  An attacker-induced client-side code execution might result in a Cross-Site Scripting (XSS) vulnerability. 

Examples of JavaScript and CSS parsing contexts relevant to MIME sniffing are:

  • <script> tags
  • <link> tags

Note: JavaScript execution via CSS injection is an edge case and it is possible only for some browsers that support it.

Now, let’s see how MIME sniffing can result in a XSS vulnerability. For an attacker to perform an XSS attack by leveraging MIME sniffing, there are certain preconditions. 

Preconditions on client side (both necessary for successful exploitation): 

  • The attacker should be able to control content in the server’s response so that malicious JavaScript can be injected.
  • The attacker should be able to introduce an executable context via HTML injection 

Preconditions on the server side (only one necessary for successful exploitation): 

  • If the server misrepresents a resource, the attack will be successful. For example, developer sets text/javascript as the value of Content-Type header in a response containing text file.  
  • The server represents a resource correctly. However, the browser’s MIME sniffing mechanism makes the resource “executable”. For example, developer sets text/plain as the value of Content-Type header in a response containing text file. Although text/plain is the correct Content-Type for a text response, browser performs MIME sniffing and makes it possible for an attacker to execute malicious JavaScript from the text file. 

Once these preconditions are satisfied, attacker can use HTML injection to inject executable context and then specify the source as the attacker-controlled resource. An example exploit payload is as follows: 

<script src=”https://example.com/attacker_controlled_resource” ></script>

Once this payload is encountered by browser, it may try to parse the response from example.com as JavaScript. As stated before, MIME sniffing algorithms vary by browser and hence it is necessary to create a proof of concept to confirm behavior of a browser and exploitability of the vulnerability. 

At this point, some of you may be wondering that MIME sniffing or a misrepresented resource is not necessary to exploit a XSS vulnerability. An attacker can specify a remotely hosted malicious JavaScript as the source of the script tag to exploit the vulnerability. Yes, you are correct. However, there is one case where MIME sniffing behavior of a browser might be the only way to exploit an XSS vulnerability. 

What if CSP is Present? 

Let’s assume that example.com deploys a Content Security Policy (CSP) that mitigates XSS exploits by disallowing scripts included from remote hosts. An example of such CSP would be:

Content-Security-Policy: default-src ‘self’; img-src https://example.com; script-src https://example.com

In this case, an attacker cannot exploit an XSS vulnerability by using inline JavaScript or remotely hosted JavaScript because the payload will be blocked by CSP. However, attacker can make use of a resource hosted on example.com and MIME sniffing to bypass CSP. 

Let’s assume that an attacker can upload text files on example.com. The attacker can write malicious JavaScript in a text file and specify the text file as the source of a script tag. 

   <script src=”https://example.com/attacker.txt” ></script>

Even if the server sets Content-Type response header as text/plain, a browser may MIME sniff the response and parse the text file content as JavaScript. CSP will not mitigate an attack in this case because example.com is a whitelisted domain. 

In the above scenarios, if the developers deploy the XCTO header and specify the correct value of the Content-Type response header, XSS attack can be mitigated. 

Other Uses of the XCTO Header

As mentioned earlier, XCTO header is useful in triggering mitigations against some other classes of vulnerabilities in Chromium based browsers. This defense mechanism is known as Cross Origin Read Blocking (CORB). 

CORB protects against two types of attacks: 

  • Cross-Site Script Inclusion (XSSI) 
  • Speculative Side Channel Attacks such as Spectre(when Site Isolationis enabled)  

However, CORB is currently available only for certain types of resources under certain conditions. The details about CORB are out of scope of this post. We will highly recommend you to read the CORB explainer documentfor more details. 

What Should Developers Do? 

Developers should always make sure that all resources served by a web application have correct Content-Type header value in response. Also, the X-Content-Type-Options header with nosniff directive should be deployed for all application responses. 

References

Categories: Uncategorized

Leave a Reply

Your email address will not be published. Required fields are marked *