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

PHAR Deserialization Attack

Security CWE-502 OWASP A8:2021 CVSS 9.8 PHP 5.0+ Advanced
debt(d5/e3/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 and psalm as tools, both specialist SAST tools, and notes automated detection is possible via code patterns like file_exists($_GET['file']). A default linter won't flag this; it requires a configured SAST tool scanning for user-controlled paths passed to file functions.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix is to validate and canonicalise all paths with realpath() before passing to file functions. This is not a single-line swap everywhere — it requires auditing all call sites where user input reaches file functions and adding path canonicalisation — but it's a small, repeatable pattern fix rather than a cross-cutting architectural refactor.

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

Closest to 'localised tax' (b3). The fix applies to PHP web and CLI contexts wherever user-controlled paths reach file functions, but once stream wrapper restrictions and input validation are in place the burden is contained. It doesn't reshape the entire architecture, though it requires ongoing discipline in any code that handles file paths.

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

Closest to 'serious trap' (t7). The misconception field directly states the canonical wrong belief: developers assume PHAR injection requires uploading a .phar file, when in reality any file function (file_exists, fopen, copy) on a user-controlled path starting with phar:// triggers deserialization — even on files masquerading as images. This contradicts the intuition that mime-type checking uploads is sufficient protection, making it a serious trap.

About DEBT scoring →

Also Known As

PHAR deserialization PHP archive injection phar:// attack

TL;DR

PHP's phar:// stream wrapper triggers deserialization of PHAR metadata on any file operation, enabling PHP object injection without unserialize().

Explanation

PHAR (PHP Archive) files embed serialized PHP metadata in their stub. Any PHP file function (file_exists(), fopen(), copy(), rename(), include()) that operates on a phar:// URI will deserialize this metadata — triggering magic methods and enabling PHP Object Injection without a call to unserialize(). An attacker who can upload a file (even one with a safe extension like .jpg) and control a path passed to a file function can exploit this. Mitigations: disable the phar stream wrapper (Phar::unlinkArchive, stream_wrapper_unregister('phar')), validate file paths strictly, and use realpath() before file operations.

How It's Exploited

An attacker uploads a JPEG image crafted with a PHAR payload in its metadata. Later, a call to file_exists('phar://uploads/evil.jpg') triggers deserialization and executes a POP gadget chain.

Common Misconception

PHAR injection requires the attacker to upload a .phar file. Any file operation using a user-controlled path — file_exists(), fopen(), copy() — can trigger PHAR deserialization if the path starts with phar://, even on an uploaded image.

Why It Matters

PHP archive files are deserialized when accessed via any stream wrapper that triggers PHP's file functions — an attacker who can upload a PHAR file and trigger a file function on it gets deserialization RCE.

Common Mistakes

  • Allowing user file uploads to be passed to file_exists(), file_get_contents(), or similar — a phar:// URL triggers deserialization.
  • Not disabling PHAR-based stream wrappers when user input is used in file paths.
  • Mime-type checking uploads but not considering that .jpg files can be valid PHAR archives.
  • Not restricting allowed PHP stream wrappers via allow_url_fopen and htaccess rules.

Code Examples

✗ Vulnerable
// User-controlled path triggers PHAR deserialization:
$file = $_GET['file'];
if (file_exists($file)) { // Attacker: ?file=phar://uploads/evil.jpg
    echo file_get_contents($file);
}
✓ Fixed
// Validate file paths — never pass user input to file functions:
function safeRead(string $userPath): string {
    // Realpath resolves .. and symlinks:
    $base = realpath('/var/www/uploads/');
    $full = realpath($base . '/' . basename($userPath));

    // Ensure resolved path is inside the allowed directory:
    if (!$full || !str_starts_with($full, $base . '/')) {
        throw new SecurityException('Path traversal attempt');
    }

    return file_get_contents($full);
}

// Disable phar stream wrapper if not needed:
stream_wrapper_unregister('phar');

// Validate file magic bytes, not extension:
$finfo = new \finfo(FILEINFO_MIME_TYPE);
$type  = $finfo->file($path); // Check actual content type

Added 15 Mar 2026
Edited 12 Jun 2026
Views 210
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 1 ping W 1 ping T 1 ping F 1 ping S 0 pings S 1 ping M 0 pings T 1 ping W 1 ping T 2 pings F 1 ping S 1 ping S 2 pings M 2 pings T 0 pings W 0 pings T 1 ping F 2 pings S 0 pings S 0 pings M 3 pings T 1 ping W 0 pings T 3 pings F 1 ping S 1 ping S 1 ping M 1 ping T 1 ping W
Claude 1
Perplexity 1
ChatGPT 104 Perplexity 29 Amazonbot 20 Scrapy 9 Google 7 Unknown AI 6 SEMrush 4 Ahrefs 3 Claude 3 Qwen 1 Majestic 1 Sogou 1 Meta AI 1 PetalBot 1
crawler 187 crawler_json 2 pre-tracking 1
DEV INTEL Tools & Severity
🔴 Critical ⚙ Fix effort: Medium
⚡ Quick Fix
Never pass user-controlled paths to file functions (file_exists, include, fopen) — validate and canonicalise all paths with realpath() first
📦 Applies To
PHP 5.0+ web cli
🔗 Prerequisites
🔍 Detection Hints
file_exists($_GET['file']) or include($_GET['path']) allowing phar:// wrapper without blocking
Auto-detectable: ✓ Yes semgrep psalm
⚠ Related Problems
🤖 AI Agent
Confidence: High False Positives: Medium ✗ Manual fix Fix: High Context: Function Tests: Update
CWE-502 CWE-694


✓ schema.org compliant