Insecure Deserialization
Also Known As
unsafe deserialization
unserialize attack
PHP object injection
TL;DR
Untrusted data passed to unserialize() can trigger PHP magic methods and lead to remote code execution.
Explanation
PHP's unserialize() reconstructs objects from a serialised string, invoking magic methods like __wakeup() and __destruct() in the process. Attackers can craft serialised payloads that exploit available classes (known as "gadget chains") to achieve arbitrary code execution, file deletion, or privilege escalation — without needing a file upload. The safe alternative is json_decode() for data exchange; if serialisation is required, restrict allowed_classes.
How It's Exploited
# Generate a POP-chain payload with phpggc:
$ phpggc Laravel/RCE1 system 'id' -b
# Set as session cookie — on unserialize(), executes 'id' on the server
$ phpggc Laravel/RCE1 system 'id' -b
# Set as session cookie — on unserialize(), executes 'id' on the server
Common Misconception
✗ Validating data before unserializing it makes the operation safe. PHP's unserialize() instantiates objects and triggers magic methods (__wakeup, __destruct) during deserialization itself — the damage happens before your validation code runs.
Why It Matters
PHP's unserialize() triggers __wakeup and __destruct magic methods on attacker-controlled objects — chained together, existing classes can be weaponised into remote code execution without any custom exploit code.
Common Mistakes
- Passing user-supplied cookies, tokens, or POST data directly to unserialize().
- Believing that a HMAC on the serialized string is sufficient — if the verification is bypassable, it isn't.
- Not using json_decode() or other format-specific parsers when PHP object serialization is not needed.
- Using unserialize() with allowed_classes but not restricting the class list tightly enough.
Code Examples
✗ Vulnerable
// Deserialising untrusted data — attacker crafts a payload
// that triggers arbitrary PHP object instantiation
$data = unserialize($_COOKIE['session']);
// Any class with __wakeup() or __destruct() in scope
// can be chained into a POP chain for RCE
✓ Fixed
// Never unserialize untrusted input — use JSON instead
$data = json_decode(base64_decode($_COOKIE['session']), true);
// If you must use serialization, whitelist allowed classes:
$data = unserialize($input, ['allowed_classes' => [SafeValueObject::class]]);
// Best: sign the payload so tampering is detected
$payload = base64_encode(json_encode($data));
$sig = hash_hmac('sha256', $payload, $_ENV['SECRET']);
$cookie = $payload . '.' . $sig;
// Verify on read:
[$payload, $sig] = explode('.', $cookie, 2);
if (!hash_equals(hash_hmac('sha256', $payload, $_ENV['SECRET']), $sig)) abort(400);
$data = json_decode(base64_decode($payload), true);
References
Tags
🤝 Adopt this term
£79/year · your link shown here
Added
15 Mar 2026
Edited
22 Mar 2026
Views
37
🤖 AI Guestbook educational data only
|
|
Last 30 days
Agents 0
No pings yet today
No pings yesterday
Perplexity 8
ChatGPT 7
Amazonbot 6
Google 4
Unknown AI 2
Ahrefs 2
SEMrush 2
Majestic 1
Also referenced
How they use it
crawler 31
pre-tracking 1
Related categories
⚡
DEV INTEL
Tools & Severity
🔴 Critical
⚙ Fix effort: Medium
⚡ Quick Fix
Replace unserialize() with json_decode(); if unavoidable use allowed_classes:false and HMAC-sign the serialised payload
📦 Applies To
PHP 5.0+
web
cli
queue-worker
🔗 Prerequisites
🔍 Detection Hints
unserialize($_GET[ or unserialize($_POST[ or unserialize($_COOKIE[ or unserialize(base64_decode(
Auto-detectable:
✓ Yes
semgrep
psalm
⚠ Related Problems
🤖 AI Agent
Confidence: Medium
False Positives: Medium
✗ Manual fix
Fix: High
Context: File
Tests: Update
CWE-502