Skip to content
>_ITDITDWeb Security Platform

Security Guides

Insecure deserialization — the danger is not the data, it is letting the data choose the type

Turning bytes back into objects can, depending on the format, let external data decide which class gets built. OWASP says attacks on deserializers have allowed denial of service, access control bypass and remote code execution — and that .NET's BinaryFormatter cannot be secured.

Published 2026-09-05 Updated 2026-09-05 Last verified 2026-09-05 7 min read

Turning received bytes back into objects becomes dangerous not because the contents are malformed. In some formats, external data gets to decide which type of object is constructed.

Why input validation arrives too late

Pure data format (JSON / XML)

external data decides values only → you receive it, then map it into your own types → validation works

Object-restoring format

external data decides values and the type → code belonging to the chosen type runs during restoration → execution never reaches your checks

Value checks happen after the object exists. In a format that chooses types, it is already over by then.

Most input validation asks whether the value is acceptable. But in an object-restoring format, the restoration itself — which class is instantiated, and what code runs along the way — is steered by external data. Something can finish executing before your validation is ever called.

It is the same shape as prototype pollution: external data deciding structure rather than values. Different name, different language, same instinct for the defense.

What goes wrong (the three OWASP lists)

Impacts observed from attacks on deserializers
Denial of service
Making the restoration itself expensive, or breaking it, to stop the application
Access control bypass
Objects representing permissions or roles constructed to the attacker's convenience
Remote code execution
Chaining the routines invoked during restoration to reach arbitrary code execution — the worst case

"Our framework doesn't do that" is not a safe assumption

This is not only a story about older languages. When we counted Next.js vulnerabilities in the NVD data, deserialization (CWE-502) sat high in the CWE distribution — because modern frameworks ship paths that deserialise the request body, Server Actions among them (do Next.js and React have a lot of vulnerabilities?). "I did not write it" does not mean "it is not there".

Two defenses

1. Do not let anything choose types (first choice)

OWASP's wording: "By switching to a pure data format like JSON or XML, you lessen the chance of custom deserialization logic being repurposed towards malicious ends."

Receive external input in a format that carries values only, then map it into your own types in your own code. That single move takes away the attacker's ability to choose what gets built. In most codebases this alone ends the problem.

2. Sign it, and refuse the unsigned

Where a custom format is unavoidable, the guidance notes you "could sign them as part of the serialization process" and then "choose not to deserialize any message which didn't have an authenticated signature".

The point is to verify before restoring. Restoring first and inspecting afterwards can already be too late, for the reason above. The order is the whole control.

Some mechanisms cannot be secured by configuration

On .NET's BinaryFormatter, OWASP states outright that "the BinaryFormatter type is dangerous and cannot be secured".

That sentence matters operationally: tools exist for which "it's fine if you use it correctly" is simply not true. Hardening options, adding validation, writing an allow-list — none of it makes it safe. The only control is not using it.

So when auditing, first separate what configuration can protect from what it cannot. Spending effort on the second category is not mitigation; it is postponement.

What OWASP names, by language

Named as dangerous (summarised from the defender's side)
PHP
Avoid unserialize(); prefer JSON
Python
pickle / c_pickle, PyYAML's load and jsonpickle are listed as dangerous
Java
Override ObjectInputStream#resolveClass() to restrict which classes can be restored
.NET
BinaryFormatter is dangerous and cannot be secured — do not use it

The common thread: a language's convenient built-in persistence machinery is precisely what must never face external data. It was built to move whole objects between parties that trust each other, and it does not assume a hostile sender.

What to check in your own code

1

List every place external data first arrives

Request bodies, cookies, sessions, queues, uploads, webhooks, caches — every point where something is stored and later restored. Sessions and caches are the easy misses: data you think of as your own can be reachable from outside depending on the path.

2

Find which of those use a type-restoring format

Search for the functions and classes named in the list above. Those are the first things to remove. If a mechanism that cannot be secured (BinaryFormatter and similar) is in use, that is the top priority — nothing but migration resolves it.

3

Move to a pure data format

Receive JSON or XML and map it into your own types in your own code. Apply schema validation during that mapping and you close unexpected keys at the same time (the same remedy as for prototype pollution).

4

Where you cannot move, bind it with a signature

If the format cannot change, sign at serialization time and verify before restoring. Make "no signature, no restore" the default. Handle the key as in what is dangerous about .env and API keys — never in the same place as the code.

5

Count the restoration your dependencies do

Even where you wrote none, a dependency may be restoring objects. CVEs classified as CWE-502 keep appearing, so put them under machine monitoring (getting started with osv-scanner / a practical CVE remediation playbook).

This site's view: ask what the input is allowed to decide, not whether the value is valid

Say "input validation" and most people picture checking the value — length, format, range. The threat in this article resolves before that point. Our framing: the real question about an input is not "is this value correct" but "how much have I let this input decide".

Only the value? The structure too (prototype pollution)? The type as well (this article)? Even where it lands (file upload vulnerabilities)? Count the decisions you have handed outside, and the dangerous places surface on their own.

Sources (primary)

  • OWASP Cheat Sheet Series, "Deserialization Cheat Sheet" — cheatsheetseries.owasp.org (the three impacts, switching to a pure data format, signing for integrity, the language-specific notes and the statement about BinaryFormatter all come from this document; checked 5 September 2026)

FAQ

QWhy is deserialization so dangerous? Isn't it just data conversion?
A

Because in some formats it is not data conversion but object reconstruction. When the information about which class to instantiate travels inside the data, an attacker gets to choose what you build. OWASP states that attacks against deserializers have been found to allow denial-of-service, access control, or remote code execution attacks. What makes it awkward is that the outcome is decided before your value checks ever run.

QWhat is the most reliable defense?
A

Restrict externally sourced data to a format that cannot choose types. OWASP puts it this way: by switching to a pure data format like JSON or XML, you lessen the chance of custom deserialization logic being repurposed towards malicious ends. If a custom format is unavoidable, the same guidance notes you could sign messages as part of the serialization process and then choose not to deserialize any message without an authenticated signature. Change the format first; bind it with a signature if you cannot.

QCan I make it safe by hardening the configuration?
A

It depends on the mechanism. OWASP states plainly that .NET's BinaryFormatter type is dangerous and cannot be secured. Some machinery does not become safe through settings or extra validation — the only resolution is to stop using it. Separate what configuration can protect from what it cannot. Tools for which "it's fine if you use it correctly" does not hold genuinely exist.

QWhat should I watch for in my language?
A

Among what OWASP names directly: in PHP, avoid unserialize() and prefer JSON; in Python, pickle / c_pickle, PyYAML's load and jsonpickle are dangerous; in Java, override ObjectInputStream#resolveClass() to restrict which classes can be restored; in .NET, do not use BinaryFormatter. The common thread is that a language's convenient persistence machinery is exactly what you must not point at external data.