Every non-empty response from a web server should contain a Content-Type response header that declares the type of content contained in the response. This declaration helps the browser understand how to process the response and can help prevent a number of serious security vulnerabilities.
Setting this header properly is more important than ever.
The Old Days
Sites caught on and started rejecting uploads that lacked the “magic bytes” indicating a JPEG/GIF/PNG at the start of the file. Unfortunately, browsers were so eager to render HTML that they would “sniff” the bytes of the file to see if they could find some HTML to render. Bad guys realized they could shove HTML+Script into metadata fields of the image binary, and the attack would still work. Ugh.
In later years, browsers got smarter and stopped sniffing HTML from files served with an image/ MIME type, and introduced a new response header:
…that declared that a browser should not attempt to sniff HTML from a document at all.
The Modern World
Chrome’s security sandbox helps ensure that a compromised (e.g. due to a bug in V8 or Blink) Renderer Process cannot steal or overwrite data on your device. However, until recently, a renderer compromise was inherently a UXSS vector, allowing data theft from every website your browser can reach.
Nearly a decade ago, Microsoft Research proposed a browser with stronger isolation between web origins, but as the Security lead for Internet Explorer, I thought it hopelessly impractical given the nature of the web. Fast forward to 2017, and Chrome’s Site Isolation project has shipped after a large number of engineer-years of effort.
Site Isolation allows the browser to isolate sites from one another in different processes, allowing the higher-privilege Browser Process to deny resources and permissions to low-privilege Renderers that should not have access. Sites that have been isolated are less vulnerable to renderer compromises, because the compromised renderer cannot load protected resources into its own process.
Of course, for this to work, sites must correctly mark their resources with the correct Content-Type response header and a X-Content-Type-Options: nosniff directive. (See the latest guidance on Chromium.org)
When Site Isolation blocks a response, a notice is shown in the Developer Tools console:
The Very Modern World
You can read more about Chrome’s efforts to combat speculative execution attacks here.
Guidance: Serve Content Securely
If your site serves JSON or similar content that contains non-public data, it is absolutely crucial that you set a proper MIME type and declare that the content should not be sniffed. For example:
Content-Type: application/json; charset=utf-8 X-Content-Type-Options: nosniff
Of course, you’ll also want to ensure that any Access-Control-Allow-Origin response headers are set appropriately (lest an attacker just steal your document through the front door!).
Thanks for your help in securing the web!