Description
Deserialization reconstructs runtime objects from data stored as bytes or text. If the input payload can specify the .NET types to create, an attacker can select gadget types available in the application or its dependencies and trigger side effects through constructors, property setters, or deserialization callbacks.
BinaryFormatter is a general-purpose object graph deserializer that includes this type information in its payloads. A binder or configuration cannot make it safe. Starting with .NET 9, the built-in runtime implementation always throws PlatformNotSupportedException when used. An unsupported compatibility package can restore the functionality, but it also restores the vulnerabilities and is not a security fix. Microsoft also identifies SoapFormatter, NetDataContractSerializer, LosFormatter, and ObjectStateFormatter as dangerous alternatives that support unrestricted polymorphic deserialization.
Json.NET's TypeNameHandling is a separate case. The default, TypeNameHandling.None, does not use the input's $type metadata to select types. With All, Auto, Objects, or Arrays, input can influence type selection, so deserializing external data requires an ISerializationBinder that enforces an explicit allow-list. Verify that the binder actually enforces that list and rejects unexpected types.
Potential impact
- Remote code execution with the application's process privileges.
- File reads and writes, network requests, and secret disclosure.
- Authentication or authorization bypass and object-state manipulation.
- Denial of service through excessive object graphs, recursion, or allocation.
Remediation
- Remove
BinaryFormatterand unrestricted legacy formatters. Do not retainBinaryFormatterby adding aSerializationBinder. - Prefer
System.Text.Jsonwith explicit DTOs for new JSON code. If polymorphism is necessary, explicitly specify only the permitted derived types and discriminators. - To migrate existing NRBF data, read records with
System.Formats.Nrbf.NrbfDecoderand manually map them to a closed set of application-defined types. Do not load or instantiate types named by the payload. - Keep Json.NET's
TypeNameHandling.Nonewhere possible. If type handling is necessary, makeISerializationBinder.BindToTypereturnnullor throw for every unexpected type, and attach the binder directly to the settings used for deserialization. - If serialized data crosses a trust boundary, verify integrity and origin with an HMAC or digital signature and plan key rotation. Prevent replay separately through nonces, sequence numbers, or expiry times. These checks supplement type restrictions but do not make
BinaryFormattersafe.
Examples
Before
Passing the request body to BinaryFormatter
using Microsoft.AspNetCore.Mvc;
using System.Runtime.Serialization.Formatters.Binary;
public object Parse([FromBody] Stream stream)
{
var formatter = new BinaryFormatter();
return formatter.Deserialize(stream);
}
Allowing Json.NET type metadata without a binder
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
public object Parse([FromBody] string json)
{
return JsonConvert.DeserializeObject(
json,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
}
After
Deserializing into an explicit DTO
using System.Text.Json;
public sealed record PaymentRequest(string AccountId, decimal Amount);
PaymentRequest request =
JsonSerializer.Deserialize<PaymentRequest>(json)
?? throw new JsonException("Missing request body");
System.Text.Json polymorphism with permitted derived types only
using System.Text.Json;
using System.Text.Json.Serialization;
[JsonPolymorphic(TypeDiscriminatorPropertyName = "$kind")]
[JsonDerivedType(typeof(CardPayment), "card")]
public abstract class Payment;
public sealed class CardPayment : Payment;
Payment payment =
JsonSerializer.Deserialize<Payment>(json)
?? throw new JsonException("Missing payment");
Reading legacy NRBF payloads without creating objects
using System.Formats.Nrbf;
SerializationRecord root = NrbfDecoder.Decode(stream);
// Validate root records with size/depth limits and a closed type mapping,
// then copy only the values needed by application DTOs.
Loading arbitrary types from the NrbfDecoder result or reconstructing a general-purpose object graph removes the protection.
Applying a restrictive binder in Json.NET
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
public sealed class KnownTypesBinder : ISerializationBinder
{
public Type BindToType(string? assemblyName, string typeName) =>
typeName == nameof(PaymentRequest)
? typeof(PaymentRequest)
: throw new JsonSerializationException("Unexpected type");
public void BindToName(
Type serializedType,
out string? assemblyName,
out string? typeName)
{
if (serializedType != typeof(PaymentRequest))
{
throw new JsonSerializationException("Unexpected type");
}
assemblyName = null;
typeName = nameof(PaymentRequest);
}
}
var settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects,
SerializationBinder = new KnownTypesBinder()
};
PaymentRequest request =
JsonConvert.DeserializeObject<PaymentRequest>(json, settings)
?? throw new JsonSerializationException("Missing request");
Prefer TypeNameHandling.None and explicit DTOs over this compatibility configuration where possible. A binder that falls back to the default type loader or passes input assembly and type names directly to Type.GetType is not an allow-list.
Classification
- CWE-502: Deserialization of Untrusted Data
- OWASP Top 10 2025 A08: Software or Data Integrity Failures
- OWASP Top 10 2021 A08: Software and Data Integrity Failures
- OWASP ASVS 5.0.0
v5.0.0-1.5.2: Safe Deserialization
References
- Microsoft: BinaryFormatter and related deserialization risks
- Microsoft: BinaryFormatter migration guide
- Microsoft: Safely reading NRBF payloads
- Microsoft CA2300: Do not use BinaryFormatter
- Microsoft CA2326: Do not use TypeNameHandling values other than None
- Microsoft CA2327: Do not use insecure JsonSerializerSettings
- Microsoft: System.Text.Json polymorphism
- Json.NET: Serialization Settings
- Json.NET: Custom SerializationBinder
- OWASP Top 10 2025 A08
- OWASP ASVS 5.0.0
- CWE-502
- Software Security Weakness Assessment Guide 2021