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

Insecure Deserialization

Security CWE-502 OWASP A8:2021 Advanced
debt(d5/e3/b5/t7)
d5 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'specialist tool catches it' (d5). The term's detection_hints.tools lists semgrep, psalm, and phpstan — these are specialist SAST tools that can flag unserialize() calls with external input patterns. Standard linters won't catch this by default, but dedicated security scanners will.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix shows this is a straightforward replacement: swap unserialize($data) with json_decode($data, true) or add ['allowed_classes' => false]. However, if serialized objects are used throughout the codebase (cookies, sessions, caches), you may need to migrate data formats across multiple locations, pushing slightly beyond a one-liner.

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

Closest to 'persistent productivity tax' (b5). Once unserialize() is used for user-facing data (cookies, URL params), it becomes a persistent security concern that affects all code touching that data path. Every feature touching deserialized user data must consider gadget chains. The common_mistakes show this pattern often spreads to multiple storage layers (Redis, database), creating ongoing review burden.

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

Closest to 'serious trap' (t7). The misconception explicitly states developers wrongly believe signing/encoding prevents the attack — but HMAC verification after deserialization is too late because object construction (and magic methods like __wakeup, __destruct) execute during unserialize(). This contradicts how signature verification works in other contexts (verify-then-use), making it a serious cognitive trap where the 'obvious' secure pattern is actually vulnerable.

About DEBT scoring →

Also Known As

PHP object injection unserialize exploit gadget chain PHP deserialization RCE

TL;DR

Deserializing attacker-controlled data can trigger arbitrary object construction and method calls — PHP's unserialize() with untrusted input enables remote code execution via gadget chains in the loaded class graph.

Explanation

PHP's unserialize() reconstructs PHP objects from a byte string, calling __wakeup() and __destruct() magic methods on every instantiated object. If the codebase contains classes whose __destruct() or __wakeup() methods perform dangerous operations (file deletion, eval, system calls) — called gadget classes — an attacker can craft a serialized string that instantiates those classes with attacker-controlled properties, achieving remote code execution or file system manipulation without ever exploiting a separate vulnerability. This technique is called a gadget chain. Popular PHP frameworks (Laravel, Symfony, Yii) and libraries (Monolog, Guzzle) have had known gadget chains. Mitigations: never deserialize untrusted data with unserialize(); use JSON (json_decode) or a safe structured format instead; if deserialization is unavoidable, pass allowed_classes as the second argument to whitelist which classes may be instantiated. The PHPGGC tool generates ready-made gadget chain payloads for audit and testing.

Watch Out

Even unserialize($data, ['allowed_classes' => false]) can be dangerous if your code then re-constructs objects from the resulting array — validate and whitelist the structure of the decoded data.

Common Misconception

Signing or encoding the serialized data (base64, HMAC) does not prevent the attack if the attacker can still inject into the serialized byte string before verification — always verify the signature before deserializing, never after.

Why It Matters

Insecure deserialization is OWASP A8 and has led to critical RCE vulnerabilities in major PHP applications — any use of unserialize() on user-supplied or cookie data is a high-severity finding.

Common Mistakes

  • Storing serialized PHP objects in cookies or URL parameters — these are trivially modified by the client.
  • Using unserialize() without the allowed_classes option — all loaded classes become potential gadgets.
  • Verifying the HMAC signature after deserializing — the dangerous object construction happens during unserialize(), before your code can check the signature.
  • Assuming private data (database, Redis) is safe — supply-chain attacks or SSRF can inject into internal stores.

Code Examples

💡 Note
The bad example deserializes a client-controlled cookie — any gadget class in the autoloaded codebase can be weaponised. Switching to JSON eliminates object instantiation entirely; allowed_classes=>false is a safer fallback when JSON is not an option.
✗ Vulnerable
// Cookie contains serialized object — attacker controlled:
$prefs = unserialize(base64_decode($_COOKIE['prefs']));
// If codebase has gadget classes, this is RCE
✓ Fixed
// Use JSON instead:
$prefs = json_decode(base64_decode($_COOKIE['prefs']), true);
if (!is_array($prefs)) {
    $prefs = [];
}
// Or if you must deserialize, whitelist no classes:
$data = unserialize($input, ['allowed_classes' => false]);

Added 10 Apr 2026
Views 88
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings W 1 ping T 0 pings F 0 pings S 1 ping S 1 ping M 0 pings T 0 pings W 0 pings T 0 pings F 1 ping S 2 pings S 4 pings M 3 pings T 1 ping W 1 ping T 5 pings F 2 pings S 2 pings S 2 pings M 6 pings T 3 pings W 2 pings T 9 pings F 6 pings S 2 pings S 2 pings M 2 pings T 1 ping W
Claude 1
ChatGPT 2
ChatGPT 45 Google 5 SEMrush 5 Perplexity 4 Claude 3 Bing 3 Ahrefs 2 Meta AI 2 Scrapy 2 Unknown AI 1 Qwen 1 Majestic 1 PetalBot 1
crawler 69 crawler_json 6
DEV INTEL Tools & Severity
🔴 Critical ⚙ Fix effort: Medium
⚡ Quick Fix
Replace unserialize($data) with json_decode($data, true); if deserialization is required, use unserialize($data, ['allowed_classes' => false])
🔗 Prerequisites
🔍 Detection Hints
unserialize( with $_COOKIE, $_GET, $_POST, $_REQUEST, or any external input as argument
Auto-detectable: ✓ Yes semgrep psalm phpstan
⚠ Related Problems
🤖 AI Agent
Confidence: High False Positives: Low ✗ Manual fix Fix: Medium Context: Function Tests: Update
CWE-502 CWE-94 CWE-915


✓ schema.org compliant