Join our Newsletter — 33% off our NHI Course

What breaks when user-controlled data is passed to PHP unserialize() without strict controls?

Passing untrusted input to unserialize() can turn data handling into code execution. PHP may instantiate attacker-chosen objects, then invoke magic methods such as __wakeup or __destruct during deserialization. That lets crafted payloads trigger file writes, method calls, SQL injection, or backdoors. The safe pattern is to avoid unserialize() for user input and use formats that do not create objects.

How PHP unserialize() turns untrusted bytes into executable behavior

The core problem is not just that the input is parsed, but that parsing can create live PHP objects. Once an attacker can influence the serialized payload, the runtime may build objects with attacker-chosen properties and then execute object lifecycle code. That shifts the trust boundary from “data” to “behavior”, which is why this function is dangerous on user input.

In practice, that means the deserializer is not a passive decoder. It can activate application logic that was never intended to run for external input, including code paths hidden in application classes or third-party libraries. The risk grows when the codebase contains gadget chains, because a harmless-looking object graph can become a trigger for side effects during reconstruction.

What kinds of breakage usually follow

The most serious outcome is object injection leading to arbitrary application behavior. Depending on the classes available, crafted payloads can trigger file writes, remote or local command execution, method calls with dangerous arguments, privilege-sensitive state changes, or database actions that were never meant to be reachable from the request body. Even without full code execution, the impact can still include data tampering and authentication bypass.

The important point for practitioners is that this is usually not a single vulnerability class in isolation. unserialize() often becomes the entry point, while magic methods, autoloading, weak type handling, and unsafe class design provide the path to impact. That is why the same bug can present as a deserialization flaw, an application logic flaw, or a post-auth compromise depending on the available gadgets.

Why safe alternatives change the security model

The safest pattern is to avoid unserialize() for any input that can be influenced by a user or remote system. Prefer formats that represent data only, such as JSON, and validate the schema before using the values. If legacy code must read serialized content, the acceptable case is tightly bounded trusted input, with strict class whitelisting and no reliance on magic methods for security-sensitive behavior.

That shift matters because the control objective changes from “make parsing work” to “prevent object creation from untrusted data”. A data-only format limits the parser to values, while strict deserialization controls reduce the chance that attacker-controlled input can instantiate unexpected classes or reach dangerous methods. In other words, the safe design removes the object lifecycle from the attack surface.

Risk and Threat Considerations

Unserialized user input is a common precondition for PHP object injection, and the actual blast radius depends on what classes, methods, and library gadgets exist in the application. Attackers typically look for reachable magic methods, file-system side effects, or code paths that can be chained into execution, persistence, or credential theft.

Failure mechanism: The application treats external bytes as trusted object state, then deserialization activates methods or side effects during object construction or teardown, allowing attacker-controlled property values to steer execution.

Impact: The result can range from denial of service and data corruption to file overwrite, unauthorized queries, backdoors, or full application compromise.

Standards & Framework Alignment

This section maps relevant standards and security frameworks to the operational risks and controls described in this guidance.

MITRE ATT&CK addresses the attack surface, OWASP ASVS, NIST SP 800-53 Rev 5 and CIS Controls v8 set the technical controls, and ISO/IEC 27001:2022 defines the regulatory obligations.

Framework Control / Reference Relevance
OWASP ASVS V8 — Authorization Unsafe deserialization can enable unauthorized actions through object gadgets.
V15 — Secure Coding and Architecture The issue is fundamentally unsafe object handling in application design.
Recommendation — Restrict sensitive actions so deserialized state cannot invoke privileged functions. Replace object-creating deserialization with data-only parsing patterns.
NIST SP 800-53 Rev 5 SI-10 — Input Validation User-controlled serialized input must be treated as untrusted data at the boundary.
SC-18 — Mobile Code Deserializer-triggered object behavior resembles executing untrusted code paths.
Recommendation — Validate and reject unsafe serialized input before application processing. Limit execution of untrusted content that can invoke application behavior.
CIS Controls v8 CIS-16 — Application Software Security The vulnerability sits in application-level input handling and unsafe deserialization.
Recommendation — Eliminate unsafe deserialization and review code for gadget-driven execution paths.
ISO/IEC 27001:2022 A.8.28 — Secure coding Secure coding guidance directly covers unsafe deserialization and object injection.
Recommendation — Build secure coding checks that forbid deserializing untrusted input.
MITRE ATT&CK T1059 — Command and Scripting Interpreter Deserialization gadgets can culminate in command execution on the host.
T1203 — Exploitation for Client Execution Payloads may trigger execution through application logic during parsing.
Recommendation — Hunt for chains that turn deserialization into interpreter or shell execution. Treat unexpected execution during parsing as a high-priority exploitation signal.

Practitioner Guidance

What to verify: Confirm whether any request parameter, cookie, header, queue message, or database field can reach unserialize() without a trust boundary. If it can, treat that path as exploitable until proven otherwise, even if the current payloads appear to be only “internal” data.

Common mistake: Teams often assume that signing or base64-encoding serialized data makes it safe. That only helps if integrity is enforced correctly and the trust boundary is truly closed; it does not make object graphs harmless, and it does not reduce gadget-chain exposure inside the application.

Practitioner takeaway: For user-controlled input, the right question is not whether unserialize() can be made to work safely in theory, but whether you can avoid object creation entirely. If you cannot, the code should be treated as a high-risk deserialization boundary and reviewed for gadget paths, magic methods, and strict class controls.