Safe deserialization in .NET starts with one rule: do not use BinaryFormatter, or related formatters, on any data you did not produce and protect yourself. Use System.Text.Json into concrete types, avoid type names supplied by the data, validate after parsing, and cap input size.
Why is BinaryFormatter the headline risk?
Microsoft's own guidance is blunt. The BinaryFormatter security guide says the type is dangerous, is not recommended for data processing, and that applications should stop using it as soon as possible, even if they believe the data is trustworthy. It also says the formatter is insecure and cannot be made secure.
The reason is how it works. The data stream names the types to create and the values to set. An attacker who controls the stream chooses which classes get instantiated, and a chain of ordinary classes with side effects in constructors, setters or finalizers can be strung into code execution. Security literature calls these chains gadgets, and CWE-502 describes the weakness as deserialization of untrusted data.
Starting in .NET 9, the in-box BinaryFormatter implementation throws exceptions on use, even with the settings that once allowed it. If your upgrade broke on that, the fix is migration, not a compatibility switch.
Which other serializers share the problem?
The same guide lists SoapFormatter, LosFormatter, NetDataContractSerializer and ObjectStateFormatter as dangerous alternatives to avoid. Other libraries are risky only when configured to read type names from the payload. Popular JSON libraries have a setting that embeds a type name in the document and honors it on read. With that on and a permissive binder, a JSON payload can pick the class to build, which brings the gadget problem back.
What should you use instead?
Pick a format where the code, not the data, decides the types. System.Text.Json is a reasonable default: it deserializes into the type you name, ignores unknown properties by default, and has no type-name-in-payload mode unless you opt into polymorphism with attributes that list allowed derived types explicitly. Other safe choices are XML with DataContractSerializer using known types you list, or a schema-based binary format with generated classes.
If you need polymorphism, use an allow list. Enumerate the derived types that are acceptable and reject everything else. Do not resolve types by name from strings.
A constructed example
A job runner accepts a work item over a queue, stored as a base64 blob. The original author used BinaryFormatter because it round-trips any class. Anyone who can write to the queue, including a compromised neighboring service, can now run code on the worker.
The migration: define a WorkItem record with a few primitive fields and a Kind enum, serialize it as JSON, deserialize into that exact type, then check each field (length limits, allowed enum values, ID format) before the worker acts. Old queue messages get a one-time converter run on a trusted machine, then the blob path is deleted.
What about data you must keep in a binary form?
Authenticate it first. Sign or MAC the payload with a key the attacker lacks, verify before parsing, and keep the parser type-restricted anyway. A signature proves who made the data, not that parsing it is safe, so treat this as a second layer.
What to do
- Search the codebase and dependencies for BinaryFormatter, SoapFormatter, LosFormatter, NetDataContractSerializer and ObjectStateFormatter.
- Replace each with System.Text.Json or another type-restricted format.
- Turn off type-name handling in any JSON library; allow list derived types if needed.
- Validate every field after deserialization, not just the shape.
- Set maximum input size and nesting depth.
- Sign or encrypt data stored where users or other services can alter it, such as cookies and queue messages.
- Keep packages updated, since many deserialization advisories sit in third-party libraries.
Dependency scanning with Safeguard can show which of your packages carry known deserialization advisories, and reachability analysis shows whether your code calls the affected path.
Quick check
Is JSON deserialization always safe?
No. Plain JSON into a concrete type is low risk. JSON that carries type names the library honors can reintroduce code execution paths.
Can I make BinaryFormatter safe with a binder?
Microsoft states it cannot be made secure. A binder narrows the surface but the guidance still says to stop using it.
Sources
- Microsoft: BinaryFormatter security guide
- OWASP Deserialization Cheat Sheet
- CWE-502: Deserialization of Untrusted Data
Last reviewed: 18 October 2026.