Deserialization with a formatter that reconstructs the type it names
ID |
csharp.insecure_deserialization |
Severity |
low |
Remediation Complexity |
medium |
Remediation Risk |
medium |
Remediation Effort |
medium |
Resource |
Misconfiguration |
Language |
CSharp |
Tags |
ASVS50:v1.5.2, CWE:502, NIST.SP.800-53, OWASP:2025:A08, PCI-DSS:6.5.1 |
Description
A payload is read with a formatter that reconstructs whatever type the payload names, or a serializer is configured to take the type from the payload. The payload then decides which classes are instantiated and what runs while they are built.
Rationale
csharp.code_injection_deserialization reports deserialization when a data path can be followed from an
external input to the read. That needs the input to be visible: a BinaryFormatter.Deserialize in a
codec or helper class, where the caller lives elsewhere, has no source to follow and is reported by
nothing.
Its severity is low because the finding says nothing about whether an attacker-controlled payload
actually reaches the call — when that can be shown, csharp.code_injection_deserialization reports it
as critical, and this finding is dropped as a duplicate.
What is reported
The type-carrying formatters, on Deserialize / UnsafeDeserialize / ReadObject:
BinaryFormatter, NetDataContractSerializer, SoapFormatter, LosFormatter, ObjectStateFormatter.
Each reconstructs whatever type the payload names and each is documented as unsafe for untrusted input.
Newtonsoft’s TypeNameHandling set to anything but None — All, Objects, Arrays, Auto — is
reported where it is configured, in an object initializer or a property assignment. That single setting
is both the defect and its fix, and the settings object it produces is normally used elsewhere.
BinaryFormatter is obsolete from .NET 5 and gated behind an opt-in switch from .NET 8, yet the type is
still present and instantiable on .NET 10 (verified) and a great deal of code targets .NET Framework. It
stays reported: it is an explicit choice on any target, which is what this rule looks for. No sink here
rests on a library default.
What counts as a restriction
A serialization binder installed on the same receiver in the enclosing method — Binder or
SerializationBinder — is the documented way to restrict what a formatter may reconstruct, and
suppresses the finding. TypeNameHandling.None is not reported, being the safe value and the default.
Examples
Reported:
var formatter = new BinaryFormatter();
return formatter.Deserialize(payload); // reported
return new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }; // reported
Not reported — a binder restricts the types:
var formatter = new BinaryFormatter();
formatter.Binder = new AllowlistBinder();
return formatter.Deserialize(payload);
Remediation
Carry the data in a format that names no types. DataContractSerializer or System.Text.Json against a
fixed type gives the same convenience without letting the payload choose the class:
var serializer = new DataContractSerializer(typeof(Order));
return (Order) serializer.ReadObject(payload);
For Newtonsoft, leave TypeNameHandling at None. Where polymorphism is genuinely needed, pair it with
a SerializationBinder that allowlists the accepted types, and treat that allowlist as
security-relevant code.