Insecure deserialization: notions and impact
When an application needs to save an object or send it to another system, it converts it into a stream of bytes or text: that is serialization. Later, it rebuilds the object from the stream: deserialization. If the stream comes from a source the user can influence and the application trusts it blindly, an attacker can craft the stream to make the application do things it never intended. This lesson is a conceptual introduction, because the topic is advanced and mostly learnt through dedicated labs.
Notions only
Exploiting deserialization flaws often leads to code execution on the server, the most serious consequence possible. For that reason we describe the concept, the recognition signs and the defences, and we provide no working crafted objects. Practise only in the dedicated PortSwigger Web Security Academy labs or in intentionally vulnerable training applications. On any other system, an unauthorised attempt is a criminal offence (in France, articles 323-1 to 323-3-1 of the Penal Code).
An analogy: flat-pack furniture
Serialization is like flat-packing a piece of furniture: you take a complex object, disassemble it into parts and instructions, and put it in a box that is easy to store or ship. Deserialization is assembling it again at the destination. Now imagine that anyone can slip a different instruction sheet into the box, and the assembler follows whatever the sheet says, including “and while you are at it, open the front door”. The assembly process is trusted, and the instructions come from a stranger.
Why programs serialize data
- Saving session data on the server or in a cookie.
- Storing objects in a cache or a queue.
- Sending objects between services or between a client and a server.
- Exporting and importing application data.
- Remote communication frameworks that call methods across machines.
Common formats are JSON and XML, which only describe plain data, and language-specific binary or text formats that can also describe objects with behaviour (their class type and their internal state). The risk is greatest with the latter, which many languages provide: Java, PHP, Python, Ruby and .NET all have native serialization mechanisms.
Where the danger comes from
Two ideas combine to create the vulnerability.
- Object reconstruction runs code. When a language rebuilds an object, it may automatically run special methods (constructors, destructors, “magic” methods) that the class defines. These methods were written for legitimate use, but they run as soon as the object is created, before the application has even looked at it.
- The attacker chooses which objects are built. If the serialized data states which class to instantiate, and its field values, then an attacker may choose any class present in the application’s code or libraries, and set its fields to values of their choice. The attacker then chains together existing classes whose methods, run in sequence, produce a harmful effect. Such a sequence of existing code is known as a gadget chain.
Notice that the attacker does not need to upload new code. They reuse the code the application already has, which makes the library collection (previous lesson) very relevant: a vulnerable chain often lives in a popular dependency.
Impact categories
| Impact | Description |
|---|---|
| Remote code execution | The most serious: running commands on the server |
| Privilege tampering | Changing serialized fields such as a role or a user identifier stored in a session object |
| Authentication bypass | Altering state held in serialized tokens |
| Denial of service | Objects designed to consume excessive memory or processing time |
| Arbitrary file or data access | Using classes that read, write or delete files with attacker-chosen parameters |
A simple, less sophisticated form of the problem does not need a gadget chain: if an application serializes an object that includes isAdmin = false into a value stored in a cookie, and then trusts it on return without a signature, the user can just change the field. This is the same lesson as in chapter 2 and chapter 5: do not trust client-held state.
How to recognise serialized data
Testers notice serialized data by looking at the cookies, hidden fields, and request and response bodies in Burp. Typical signs:
- Values that look like structured text with type names and lengths, quite unlike plain JSON.
- Long encoded strings (often Base64) that, once decoded, reveal readable class names or recognisable binary headers.
- File extensions, headers or parameters that mention serialized objects or specific frameworks.
- Content types that are not plain JSON, XML or form data.
Burp’s Decoder module is handy here: you can decode a value from Base64 and see whether readable structure appears. The decoded structure often reveals which language and framework is behind the application, which helps you decide what to read next in the documentation and the Academy materials.
Observation: cookie "profile" contains a long Base64 string.
Decoded structure: shows a class name, field names and values such as role=user.
Conclusion: the application stores serialized state in the cookie.
Question: is it signed or encrypted? Does the server verify it before deserializing?
Testing mindset
Even without building a complex object, a tester can raise useful questions in an authorised lab or engagement:
- Is there serialized data coming from the client at all?
- If so, is its integrity protected by a signature or message authentication code verified before parsing?
- Does altering a harmless field (like a display preference) change the application’s behaviour in a way that shows it trusts the data?
- What serialization format and libraries are in use, and are they known to have deserialization issues?
The answer to the first question already guides the report: “the application accepts serialized objects from the client” is a finding of risk, even without an exploit. Where the next steps require crafting objects, rely on the guided Academy labs, which teach the technique within their safe environment.
Defences
- Do not deserialize untrusted data with language-native object serialization. This is the most reliable fix.
- Use simple data formats that carry only data, such as JSON, and map them to known, fixed structures with explicit validation, instead of letting the stream choose arbitrary classes.
- Verify integrity first. If data must travel via the client, sign it (or encrypt and authenticate it) with a secret key kept on the server, and check the signature before any parsing.
- Keep state on the server. Store the object in the server-side session and give the client only an opaque identifier.
- Use allowlists of permitted classes where the framework offers deserialization filters.
- Run with least privilege and isolate components that must process such data, so that a compromise remains contained.
- Keep libraries updated to remove known gadget chains, and monitor for deserialization errors and unusual exceptions.
The OWASP Cheat Sheet Series has a dedicated deserialization page with language-specific advice at owasp.org.
Tip
Not every serialized value is dangerous. Plain JSON with validated fields is a normal and safe design. What makes the risk is the combination of native object serialization and trust in client-controlled input.
Your turn: classify the risk
For each case, say whether you consider the design risky, and why: (1) a cookie containing the user’s display preference as JSON, validated and signed by the server; (2) a hidden field containing a Base64 string that decodes to a native serialized object with a role field, and no signature; (3) a server-side session where the client only holds a random identifier.
See a solution
- Low risk. Plain data, validated, with a verified signature: the server does not trust unchecked input.
- High risk. Native object serialization controlled by the client without integrity protection: field tampering is trivial, and parsing may trigger deeper issues depending on the language and libraries.
- Low risk. No client-controlled serialized data; the object stays on the server.
Write the second case as a draft finding: “Application accepts client-supplied serialized objects without integrity checks”, with the recommendation to keep state on the server or to sign and validate it, and to avoid native deserialization of untrusted data.
Key takeaways
- Deserialization rebuilds objects from a stream; when the stream is attacker-controlled, objects, fields and even code paths may be attacker-chosen.
- Gadget chains reuse existing code in the application and its libraries, which links this risk to dependency management.
- Impacts range from tampering with roles and tokens to remote code execution and denial of service.
- Recognise serialized data in cookies, hidden fields and bodies, often Base64-encoded; Burp’s Decoder helps.
- Defend by not deserializing untrusted data, using simple data formats, verifying integrity first, and keeping state on the server.
You have now toured the major families of web vulnerabilities. In the final chapter, you will learn to turn your notebook into something that gets problems fixed: a clear report, a priority and a responsible disclosure.