serialize() / unserialize()
debt(d5/e3/b5/t7)
Closest to 'specialist tool catches' (d5), semgrep/psalm/phpstan rules can flag unserialize() on untrusted input, but it's silent at runtime and not caught by default linters.
Closest to 'simple parameterised fix' (e3), quick_fix is replacing serialize/unserialize with json_encode/json_decode — a pattern swap, but it touches each call site and may require data migration if serialized data is already persisted.
Closest to 'persistent productivity tax' (b5), applies across web/cli/queue contexts; once serialized data is stored in DB/cookies/sessions, the format choice shapes ongoing maintenance and migration work.
Closest to 'serious trap' (t7), the misconception that serialize() is safe for caching contradicts safer norms in other languages — unserialize() silently instantiates arbitrary classes and triggers magic methods, enabling object injection in a way most devs don't anticipate.
Also Known As
TL;DR
Explanation
unserialize() reconstructs PHP objects from a string, invoking magic methods like __wakeup() and __destruct() in the process. If the serialised payload is attacker-controlled, they can craft a Property Oriented Programming (POP) chain using classes already loaded in the application to achieve arbitrary code execution — a PHP Object Injection attack. Never call unserialize() on user-supplied input. Use JSON (json_encode/json_decode) for data exchange; if serialisation is required, use authenticated, signed payloads or a safe serialisation library.
Common Misconception
Why It Matters
Common Mistakes
- Unserializing any user-controlled data — cookies, URL parameters, database values from untrusted sources.
- Using serialize() for data exchange between systems — use JSON instead; it cannot trigger PHP object instantiation.
- Not using allowed_classes option in unserialize() to restrict which classes can be instantiated.
- Storing serialized data in cookies — the cookie is user-controlled and can be replaced with a crafted payload.
Code Examples
$obj = unserialize($_COOKIE['data']); // attacker-controlled
$data = json_decode(base64_decode($_COOKIE['data']), true); // use JSON for simple data