Safeguard
Security Guides•

Deserialization risk compared: Python, Java, .NET and PHP

Which deserialization calls are risky in Python, Java, .NET and PHP, and what safe default to use in each, based on the OWASP cheat sheet and vendor docs.

Safeguard Security Team
3 min read

The risky pattern is the same in every language: a deserializer that lets the data choose which types to build. The fix is also shared: use data only formats, fix the target type in code, and verify data before reading it. The calls to watch differ by language.

The common rule

OWASP's guidance is to avoid native deserialization of untrusted data. Where that is not possible, restrict which types can be created, bound resource use and verify the data before reading it. A deserializer can only instantiate types it knows about, so keep it from choosing the target type.

Python

  • Risky: pickle.loads, and yaml.load with the full loader. The Python docs say pickle can execute arbitrary code while loading, and PyYAML says yaml.load is as powerful as pickle.load.
  • Also named by OWASP: jsonpickle.decode on untrusted input.
  • Safer: JSON, yaml.safe_load.

Details: Python pickle and untrusted data and PyYAML safe_load.

Java

  • Risky: ObjectInputStream reading Serializable objects. Look for readObject, readResolve, readExternal, readUnshared, XMLDecoder and XStream.fromXML.
  • Signs in traffic: the bytes AC ED 00 05, Base64 rO0 or the content type application/x-java-serialized-object.
  • Safer: JSON with explicit types, serialization filters with an allowlist and size limits where native serialization is unavoidable.

Details: Java allowlist filters.

.NET

  • Risky: BinaryFormatter, which Microsoft says is insecure and cannot be made secure. Also SoapFormatter, LosFormatter, NetDataContractSerializer and ObjectStateFormatter.
  • Watch in Json.NET: TypeNameHandling, which OWASP says to keep at None, or use a custom SerializationBinder as an allowlist.
  • Safer: System.Text.Json, XmlSerializer, DataContractSerializer.

Details: BinaryFormatter and its replacements.

PHP

  • Risky: unserialize() on data from users.
  • Safer: json_decode() and json_encode() for data exchanged with users, per OWASP.

Three habits that carry across

  1. Fix the type in code. Deserialize into a specific data class, never into a type named by the payload.
  2. Sign when you control both ends. OWASP says to sign data before deserializing and reject messages without a verified signature. Signing proves origin, not safety, if the key or signer is compromised.
  3. Treat "internal" data as untrusted. Queues, caches and shared storage cross trust boundaries more often than people expect.

Finding it in your code

Search for the calls above, then trace each input back to its source. Dependencies matter too, since a library may deserialize on your behalf. Safeguard's SBOM and dependency scanning and reachability analysis help you see which libraries in your tree carry known deserialization advisories and whether your code reaches them. A free scan is at gold.safeguard.sh/scan.

Quick check

Is JSON always safe to deserialize?

It does not run code by default, but polymorphic type hints in some libraries can bring the risk back. Keep types fixed and validate the content.

Does signing remove the risk?

It prevents outsiders from tampering. It does not help if the signer is untrusted or the key leaks.

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.