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

ImpactExample
Remote code executionThe server runs commands chosen by the attacker
Privilege tamperingA stored role changes from "user" to "admin"
Replay attacksAn old object returns and repeats a past action
Denial of serviceA 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

  1. Avoid deserializing data from untrusted sources whenever possible.
  2. Prefer JSON or similar plain formats over native object formats.
  3. Sign or encrypt serialized data that must travel through the browser.
  4. Allow only a short list of expected classes when a framework requires object deserialization.
  5. Run deserialization code with minimal privileges and inside isolated environments.
  6. Log deserialization errors and alert on repeated failures.
  7. 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.