flawopen.com/Insecure Deserialization/PHP unserialize()
Is PHP unserialize() safe for untrusted input?
Do not pass untrusted input to unserialize(), even when allowed_classes is set. PHP may restore objects and invoke __unserialize() or __wakeup(); an attacker may exploit classes available to the application or autoloader. Use JSON for untrusted data. With associative decoding, JSON objects become arrays and cannot instantiate application classes with magic-method behavior.
<?php // Cookie content is controlled by the client. $input = $_COOKIE['prefs'] ?? ''; $data = unserialize($input); // Restoring an object may invoke its // __unserialize() or __wakeup() method. // Later object destruction may invoke __destruct(). // The risk depends on available classes, // their serialized state, and reachable behavior.
<?php
$input = $_POST['prefs'] ?? null;
if (!is_string($input)) {
http_response_code(400);
exit;
}
try {
$data = json_decode(
$input,
true,
512,
JSON_THROW_ON_ERROR
);
} catch (JsonException $e) {
http_response_code(400);
exit;
}
if (!is_array($data)) {
http_response_code(400);
exit;
}
// Validate required keys and value types here.
What a PHP object-injection chain requires
An object-injection risk is not automatic code execution. The serialized value must name a class PHP can resolve, the class must be permitted by the unserialization options, and its restoration or later use must reach behavior with a security impact. PHP may call __unserialize() or __wakeup() while restoring an object; __destruct() can run when the object is destroyed, and __toString() runs only when the object is used in a string context.
A property-oriented programming (POP) chain links behavior already present in the application or its loaded dependencies. Exploitability depends on the actual classes, serialized properties, call path, and runtime conditions; a dangerous gadget chain must be demonstrated for the target rather than assumed from the presence of unserialize() alone.
Why string filtering and allowed_classes are not a complete fix
Blocking a visible marker such as O:, filtering class names, or limiting string length does not safely validate PHP serialization. More importantly, PHP explicitly warns against passing untrusted input to unserialize() regardless of allowed_classes. When a class is disallowed, PHP creates a __PHP_Incomplete_Class object; the serialized structure is still parsed, so validate output shape and bound depth if handling trusted legacy data. Prefer JSON for client-provided state.
FAQ
No. It prevents normal application classes from being restored; disallowed objects become __PHP_Incomplete_Class. PHP still warns not to pass untrusted input to unserialize() regardless of this option. Treat it only as defense in depth for trusted legacy data, validate the result, and set a bounded max_depth. PHP documents that this option does not affect enumerations.
It can establish integrity for data generated by your application if the key remains secret and verification with hash_equals() happens before deserialization. Reject invalid signatures before parsing. An HMAC does not encrypt data or prevent replay; add expiry or nonce checks when the application needs freshness. Do not use signing to legitimize arbitrary user-supplied serialized input.
They share the broad risk of restoring objects from serialized input, but their formats, hooks, class-resolution rules, and mitigations differ. Assess each runtime and its available classes separately; do not assume that a gadget chain for one platform works on another.
References
- PHP manual — unserialize()
- PHP manual — magic methods
- PHP manual — JSON decoding
- PHP manual — __PHP_Incomplete_Class
- OWASP — PHP object injection
- CWE-502 — Deserialization of Untrusted Data
- Is Python's pickle safe for untrusted data?
- Is Java's ObjectInputStream safe to use on untrusted data?