security xss ai_generated true

XSS attack succeeds via uploaded file because browser sniffs content type instead of respecting Content-Type header

ID: security/content-type-sniffing-xss

Also available as: JSON · Markdown
90%Fix Rate
92%Confidence
3Evidence
2023-01-01First Seen

Version Compatibility

VersionStatusIntroducedDeprecatedNotes
any active

Root Cause

Browsers may ignore Content-Type header and 'sniff' the actual content. A file served as text/plain but containing <script> tags may be executed as HTML. The X-Content-Type-Options: nosniff header prevents this but is often missing.

generic

Workarounds

  1. 95% success Add X-Content-Type-Options: nosniff header to all responses
    response.headers['X-Content-Type-Options'] = 'nosniff'  # prevents MIME sniffing
  2. 92% success Serve user uploads from a separate domain/subdomain
    Serve from uploads.example.com, not example.com. Same-origin policy prevents uploaded content from accessing main site cookies/data.
  3. 88% success Set Content-Disposition: attachment for all user-uploaded files
    response.headers['Content-Disposition'] = 'attachment'  # forces download instead of rendering

Dead Ends

Common approaches that don't work:

  1. Serve user-uploaded files with correct Content-Type and assume safety 88% fail

    Without nosniff header, browsers may override Content-Type based on content analysis. A .txt file with HTML content may be rendered as HTML.

  2. Validate file extension to prevent malicious uploads 82% fail

    Extension validation is trivially bypassed. A file named 'image.png' can contain HTML/JS. Content-Type from extension doesn't prevent sniffing.