Deserialization Gadget Chains
debt(d5/e5/b5/t7)
Closest to 'specialist tool catches it' (d5). The detection_hints list semgrep, psalm, and phpggc as tools that can identify unserialize() calls on user-controlled input. These are specialist static analysis tools, not default linters — requires deliberate tooling setup to catch.
Closest to 'touches multiple files / significant refactor' (e5). The quick_fix says 'Replace unserialize() with json_decode() everywhere' — while each individual fix is a simple replacement, serialized data formats are often embedded in session handling, caching layers, queue systems, and database storage, requiring coordinated changes across multiple components and potentially data migration.
Closest to 'persistent productivity tax' (b5). Applies to both web and cli contexts across PHP 5.0+. Once unserialize() patterns are established in session handling, caching, or inter-service communication, every new feature touching those systems must maintain awareness of the serialization format. Not architectural-level burden, but a consistent tax on data handling throughout the codebase.
Closest to 'serious trap' (t7). The misconception field explicitly states that developers believe 'Object injection is only dangerous if the codebase has obviously dangerous code' — this contradicts reality because gadget chains exploit legitimate framework classes (Guzzle, Monolog, Laravel) with dangerous magic methods. Developers familiar with injection attacks elsewhere expect to see the danger in their own code, not in trusted dependencies.
Also Known As
TL;DR
Explanation
A gadget chain strings together existing PHP class methods invoked during deserialisation. When unserialize() processes an object, PHP calls magic methods: __wakeup (on deserialisation), __destruct (on garbage collection), __toString (if cast to string), __get/__set (property access). If the codebase contains classes with dangerous operations in these methods (file writes, eval, shell commands), an attacker crafts a serialised payload that instantiates those classes with controlled properties, chaining the magic methods into RCE. Tools: PHPGGC (PHP Generic Gadget Chains) generates payloads for popular frameworks.
Common Misconception
Why It Matters
Common Mistakes
- unserialize() on any user-controlled data — use JSON instead.
- Thinking allowed_classes: false is sufficient — it prevents object instantiation but some PHP versions have bypass techniques.
- Not patching deserialization vulnerabilities in dependencies — many CVEs in Composer packages.
- Using serialize() to store session data in user-accessible locations.
Code Examples
// Direct unserialize of user input — critical RCE vulnerability:
$data = unserialize(base64_decode($_COOKIE['session_data']));
// Attacker sends PHPGGC payload for Laravel gadget chain
// __destruct chain → file_put_contents → webshell written
// → full server compromise
// Never unserialize user input — use JSON:
$data = json_decode(base64_decode($_COOKIE['session_data']), true);
if (!is_array($data)) throw new InvalidSessionException();
// If unserialize is unavoidable (legacy data):
$data = unserialize($serialized, ['allowed_classes' => false]);
// false = no objects instantiated, only scalar types
// HMAC-sign the serialized data to detect tampering:
$payload = $data . '|' . hash_hmac('sha256', $data, SECRET_KEY);
// Verify before deserializing:
[$data, $mac] = explode('|', $payload, 2);
if (!hash_equals(hash_hmac('sha256', $data, SECRET_KEY), $mac)) die('Tampered');