Content-Type and nosniff Bypass: When Browsers Still Execute
Browsers used to guess file types. That guess—MIME sniffing—turned a text/plain upload into HTML. X-Content-Type-Options: nosniff was supposed to end the party. It mostly did, on modern engines, for many navigations. Gaps remain when headers are missing, wrong, or ignored by a particular response path.
Your job is not to recite the history. It is to find a response that still activates as HTML or script in a browser the program cares about. Header inventories beat payload spam here: list every user-influenced response, then test the weak ones.
What to inspect on every upload and reflect
For user-controlled bodies served back to browsers, check:
Content-Type(declared vs actual bytes)X-Content-Type-Options: nosniffContent-Disposition(inline vs attachment)- Whether the URL is same-site with cookies
A JSON API that reflects input as application/json with nosniff is usually fine even if the body contains <script>. Flip any of those headers and retest navigation to the raw endpoint.
Missing nosniff alone is often Informative. Pair it with a sniffable body and a browser that still treats it as active content—or with an old WebView in scope—and you have a real issue.
Static asset buckets inherited from older projects are frequent misses: the app gained security headers, the bucket policy did not. Diff headers between www and cdn on the same object class before you write the finding off.
Bypass angles worth trying
Some apps add nosniff on the HTML shell but not on the CDN object store. Some set Content-Type: text/html on error pages that echo input. Some use filename="x.html" with a generic type. Try polyglot files: HTML that is also valid enough to pass an image validator, served from a media route.
Cross-browser notes matter. Chromium, Firefox, and Safari disagree on edge sniffing cases. Document versions. If only a deprecated browser executes, write that down before claiming critical.
I've watched reports fail because the PoC used curl headers as proof. Browsers decide execution. Always include a browser repro.
Also compare authenticated versus anonymous fetches of the same object. A signed media URL might add headers that the public CDN path omits—test both, and record which one executes.
Impact language
If navigating to /media/123 runs script on a cookieless host, impact may stop at stored HTML on that origin. If the host shares cookies with the main app, talk session risk. If the response is only embedded as an image, script may never run—say that.
Error handlers and debug endpoints deserve the same header checklist. A perfect upload pipeline can still lose if /api/export?format=raw echoes bodies as HTML without nosniff.
Recommend setting nosniff globally, using correct types, forcing attachment for untrusted files, isolating uploads on a distinct origin, and adding CSP on any domain that can render HTML. Middleware that injects security headers once at the edge beats hoping every microservice remembers them.
MIME issues feel like configuration nits until one missing header turns an upload into a wormable stored XSS. Check the headers first; write the payload second.