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

File Extension Bypass

security CWE-434 OWASP A4:2021 CVSS 9.8 PHP 5.0+ Intermediate
debt(d5/e5/b3/t7)
d5 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'specialist tool catches it' (d5). The detection_hints list Semgrep as the tool, with a specific code pattern targeting pathinfo() or strrchr() without MIME validation. This is not caught by the compiler or a default linter — it requires a configured SAST rule (Semgrep) to flag the pattern, placing it squarely at d5.

e5 Effort Remediation debt — work required to fix once spotted

Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix requires replacing extension-based validation with finfo_file(), renaming files to UUID filenames, moving the storage location outside the webroot, and auditing web server configuration for executable extensions. This is more than a one-liner — it touches the upload handler, storage logic, and server config — but is contained within the file-upload subsystem rather than being codebase-wide, placing it at e5.

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

Closest to 'localised tax' (b3). The applies_to scope is web context file upload handling. The burden is real but isolated: only the upload and file-serving components carry the weight of proper MIME validation, UUID naming, and webroot segregation. The rest of the codebase is largely unaffected, keeping this at b3.

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

Closest to 'serious trap' (t7). The misconception field explicitly states that developers believe a blocklist of dangerous extensions is sufficient, when in reality servers can execute .php5, .phar, and even .jpg files as PHP under certain configurations. The common_mistakes reinforce multiple independently plausible wrong assumptions (last-extension-only checks, null byte blindness, no magic-byte verification). This contradicts intuitive reasoning about how file type checking should work and is a well-documented but frequently missed gotcha, scoring t7.

About DEBT scoring →

Also Known As

extension bypass double extension trick upload bypass

TL;DR

Circumventing upload filters via double extensions, null bytes, or alternate MIME types to upload executable files disguised as safe ones.

Explanation

Upload filters that check only file extension are easily bypassed: shell.php.jpg may execute as PHP on misconfigured servers; %00 null bytes truncated extension checks in older PHP; shell.pHp evades case-sensitive checks; .phtml, .php5, and .phar are overlooked PHP-executable extensions. Correct validation: allowlist safe MIME types verified server-side with finfo_file(), store uploads outside the document root, serve through a PHP controller that sets Content-Type explicitly, and never rely on extension alone. Rename uploaded files server-side to strip any attacker-controlled extension.

How It's Exploited

A filter permits only .jpg. Attacker uploads shell.php.jpg — an Apache server with AddHandler php-script .php executes it as PHP regardless of the .jpg suffix.

Common Misconception

A blocklist of dangerous extensions (.php, .phtml) is sufficient. Servers can be configured to execute .php5, .phar, and even .jpg files as PHP — allowlisting known-safe extensions is far more reliable.

Why It Matters

Attackers upload PHP files with double extensions (.php.jpg), null bytes (.php%00.jpg), or alternative PHP extensions (.php5, .phtml) to bypass naive extension-based upload filters.

Common Mistakes

  • Checking only the last extension — file.php.jpg passes a .jpg check.
  • Not stripping null bytes from filenames — .php%00.jpg becomes .php on old PHP.
  • Allowlisting by extension without verifying actual file content via magic bytes.
  • Web server configured to execute .php5, .phtml, .phar as PHP — all must be blocked.

Code Examples

✗ Vulnerable
// Extension check bypassable:
$ext = strtolower(pathinfo($_FILES['f']['name'], PATHINFO_EXTENSION));
if ($ext === 'jpg') move_uploaded_file(...); // file.php.jpg passes!

// Safe approach:
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($_FILES['f']['tmp_name']);
$allowed = ['image/jpeg', 'image/png', 'image/gif'];
if (!in_array($mime, $allowed, true)) die('Blocked');
✓ Fixed
// Validate content type from file content, not extension:
function validateUpload(string $tmpPath): string {
    $finfo    = new \finfo(FILEINFO_MIME_TYPE);
    $mimeType = $finfo->file($tmpPath);

    $allowed = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
    if (!in_array($mimeType, $allowed, true)) {
        throw new InvalidFileTypeException("Disallowed type: {$mimeType}");
    }

    // Store with a safe server-generated name:
    $ext      = ['image/jpeg'=>'jpg','image/png'=>'png','image/gif'=>'gif','image/webp'=>'webp'][$mimeType];
    $safeName = bin2hex(random_bytes(16)) . '.' . $ext;
    return $safeName;
}

Added 15 Mar 2026
Edited 22 Mar 2026
Views 107
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings W 0 pings T 0 pings F 2 pings S 0 pings S 0 pings M 0 pings T 0 pings 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 0 pings F 2 pings S 1 ping S 2 pings M 1 ping 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
No pings yet today
No pings yesterday
ChatGPT 39 Amazonbot 16 Perplexity 15 SEMrush 5 Ahrefs 4 Google 3 Majestic 2 Unknown AI 2 Bing 1 Meta AI 1
crawler 85 crawler_json 2 pre-tracking 1
DEV INTEL Tools & Severity
🔴 Critical ⚙ Fix effort: Medium
⚡ Quick Fix
Validate file type using finfo_file() not the extension; store with a UUID filename and never serve as executable; keep outside webroot
📦 Applies To
PHP 5.0+ web
🔗 Prerequisites
🔍 Detection Hints
Extension check via pathinfo($file,'extension') or strrchr($file,'.') without MIME validation
Auto-detectable: ✓ Yes semgrep
⚠ Related Problems
🤖 AI Agent
Confidence: High False Positives: Medium ✗ Manual fix Fix: Medium Context: Function Tests: Update
CWE-434 CWE-646

✓ schema.org compliant