Safeguard
Security Guides•

How do you review code that deserializes untrusted data?

Flag native deserialization of untrusted input, prefer plain data formats, verify integrity first and allow list classes. Language red flags for reviewers.

Safeguard Security Team
4 min read

Treat any deserialization of data from outside your trust boundary as a finding until proven otherwise. Ask for a plain data format such as JSON, an integrity check before parsing, and an allow list of types that may be created. Then look for the language specific red flags below.

Why it is risky

The OWASP Deserialization Cheat Sheet says deserializing untrusted data can lead to denial of service, access control bypass or remote code execution. Native serialization formats can rebuild arbitrary object graphs, and attackers chain existing classes in your dependencies to reach dangerous behavior.

Core advice

AdviceWhat it looks like in review
Prefer plain data formatsJSON or XML into simple data objects, not native object streams
Verify integrity firstA signature checked before any parsing
Allow list classesAn explicit list of types, not a block list, because new gadget classes keep appearing
Bound resourcesLimits on depth, object count, array size and input size
Keep libraries currentNo default polymorphic type handling left on

Red flags by language

These come from the cheat sheet.

  • Java: review readObject, readObjectNoData, readResolve, readExternal and readUnshared on ObjectInputStream. Use serialization filters with class allow lists. Overriding resolveClass alone is not enough. A stream header of AC ED 00 05, or rO0 in Base64, signals Java serialization.
  • Python: never unpickle untrusted data. Be careful with jsonpickle decode and unsafe YAML loaders, and use yaml.safe_load for untrusted YAML.
  • PHP: audit every unserialize call and how its input arrives. Replace it with json_decode for data exchange.
  • .NET: do not use BinaryFormatter, which Microsoft states cannot be secured. Avoid TypeNameHandling other than None in JSON.NET, and do not let the data stream choose the target type.
# red flag
obj = pickle.loads(request.data)

# better: parse plain data, then build your own object
data = json.loads(request.data)
obj = Order(id=data["id"], qty=int(data["qty"]))

Steps for a review

  1. Search the diff for the calls above and for the formats they imply.
  2. Trace where the input comes from. Cookies, message queues, caches and uploaded files all count as input.
  3. If the source is trusted, ask how trust is enforced. A signature is stronger than a network boundary.
  4. If a native format is needed, ask for class allow lists and resource limits.
  5. Check dependency versions, because many deserialization exploits depend on library classes being present.

The dependency angle

Deserialization bugs often sit in a library in your tree, not in your own code, so a dependency bump that mentions deserialization in its notes is worth a read. Reachability matters here: whether your code actually reaches the vulnerable path decides how urgent it is.

Try it yourself

Open the reachability demo and compare the raw list of findings with the filtered one. Look at how reachability separates libraries that are present from vulnerable code your app calls.

Click through it at your own pace, or open it full screen.

Where Safeguard fits

Safeguard offers reachability analysis and dependency scanning, and you can try the free scan at gold.safeguard.sh/scan.

Quick check

Is a block list of dangerous classes enough?

No. OWASP says block lists are weaker because new gadget classes keep appearing. Use an allow list.

What replaces unserialize in PHP?

OWASP suggests json_decode for data exchange.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.