OWASP Insecure Deserialization
Applications often turn objects into text or bytes so they can store or send them. Serialization performs that conversion, and deserialization rebuilds the object. Insecure deserialization happens when an application rebuilds objects from data that an attacker can change. The rebuilt object may run harmful code or break business rules.
Everyday Comparison
Flat-pack furniture explains the idea. The factory packs a wardrobe into a box with instructions. The buyer follows the instructions to rebuild it. Now imagine a stranger swaps the instructions with a plan that builds a trap. A buyer who follows any instructions without checking them gets hurt. Deserialization of untrusted data works the same way.
The Data Journey
Server creates object
|
↓ serialize
+----------------+
| Data string | → stored in a cookie, hidden field, or message queue
+----------------+
|
↓ user or attacker can edit the string
+----------------+
| Modified data |
+----------------+
|
↓ deserialize
Server rebuilds object and trusts it
|
↓
Unexpected behavior or code execution
Why the Risk Runs High
Some serialization formats do more than store plain values. They can trigger methods while rebuilding an object. Attackers chain existing classes inside the application to reach powerful actions. Researchers call these chains gadget chains. The attacker writes no new program; the application supplies the building blocks.
Possible Impacts
| Impact | Example |
|---|---|
| Remote code execution | The server runs commands chosen by the attacker |
| Privilege tampering | A stored role changes from "user" to "admin" |
| Replay attacks | An old object returns and repeats a past action |
| Denial of service | A crafted structure exhausts memory or processor time |
Risky Versus Safer Choices
Risky Choice
Python's pickle format and native object serialization in several languages rebuild arbitrary objects. Reading them from user-controlled sources invites trouble.
import pickle
data = request.cookies["profile"]
profile = pickle.loads(data) # never do this with user data
Safer Choice
Plain data formats such as JSON carry only simple values. The application then builds its own objects from validated fields.
import json
data = request.cookies["profile"]
fields = json.loads(data)
name = fields.get("name", "")
if not isinstance(name, str) or len(name) > 50:
raise ValueError("Invalid profile")
Protect Data Integrity
Sometimes an application must send state to the browser. Sign the data with a secret key so that tampering becomes detectable. A message authentication code, such as HMAC, produces a signature that the server verifies before it reads anything.
data + secret key → HMAC → signature Sent to browser: data . signature Returned to server: verify signature first, then read data
Keep the secret key on the server only. Rotate it on a schedule.
Best Practices
- Avoid deserializing data from untrusted sources whenever possible.
- Prefer JSON or similar plain formats over native object formats.
- Sign or encrypt serialized data that must travel through the browser.
- Allow only a short list of expected classes when a framework requires object deserialization.
- Run deserialization code with minimal privileges and inside isolated environments.
- Log deserialization errors and alert on repeated failures.
- Keep libraries updated, since many known gadget chains receive patches.
Key Takeaways
- Deserialization rebuilds objects from stored data and trusts the data too easily.
- Attackers modify the data and reuse code that already exists in the application.
- Plain formats, signatures, and strict allowlists reduce the risk.
- The safest design never deserializes untrusted input.
