flawopen.com/Insecure Deserialization/TypeNameHandling
Newtonsoft.Json TypeNameHandling: safe configuration for untrusted JSON?
Ordinary JSON describes data; your code chooses the .NET type. With TypeNameHandling enabled, a $type value can influence type resolution during deserialization. Keep it at None for untrusted input. If polymorphism is required, validate incoming types with a narrow ISerializationBinder or use a closed discriminator mapping declared in code.
var settings = new JsonSerializerSettings {
// Any non-None mode needs a strict binder
TypeNameHandling = TypeNameHandling.Auto,
// No binder: input $type can resolve CLR types
};
var value = JsonConvert.DeserializeObject<Base>(
untrustedJson, settings);
// Safest default for untrusted JSON var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.None, }; // For polymorphism, dispatch over a closed set: var token = JObject.Parse(json); Base value = token["kind"]?.ToString() switch { "circle" => token.ToObject<Circle>(), "square" => token.ToObject<Square>(), _ => throw new JsonException("Unknown kind"), }; // System.Text.Json also supports opt-in derived // types through [JsonDerivedType] (.NET 7+).
Why developers enable TypeNameHandling
Polymorphic data is the common reason: a collection declared as Shape may need to round-trip as Circle or Square. Json.NET can write and read type names through $type. On untrusted input, that metadata can influence which runtime type is resolved; the risk depends on the allowed types and their deserialization behavior. A dangerous chain requires a reachable type with side effects, not merely the presence of JSON.
The trust boundary should run in the opposite direction: application code selects a subtype from a closed set of validated discriminator values. System.Text.Json supports opt-in polymorphism using attributes such as [JsonDerivedType]; it does not activate arbitrary CLR type names from JSON. See the separate BinaryFormatter guidance for a related deserialization boundary.
Is a custom ISerializationBinder enough?
A strict ISerializationBinder is Json.NET’s required defense when TypeNameHandling is not None for external input. It should map only a small set of exact, application-owned type names to expected types and reject everything else. Review each permitted type and its converters, constructors, setters, and callbacks. The binder narrows type resolution; it does not make arbitrary types harmless. Prefer None plus explicit discriminator dispatch when you control the protocol.
FAQ
Not by itself. Auto can read $type metadata during deserialization. For external data, set TypeNameHandling.None, or use a strict ISerializationBinder that permits only reviewed types. Do not treat Auto as a safe middle ground.
It supports opt-in polymorphism with a discriminator and derived types declared in code, for example [JsonDerivedType] on .NET 7 and later. The declared set is constrained by application code rather than arbitrary CLR type names in the payload.
Search the whole solution for TypeNameHandling, SerializationBinder, and JsonConvert.DeserializeObject. Include ASP.NET startup and shared settings, then trace which endpoints pass external JSON to each configuration. Confirm every non-None path has a strict binder.
References
- Json.NET — Serialization Settings: TypeNameHandling and SerializationBinder
- Json.NET API — ISerializationBinder
- Microsoft Learn — Polymorphic serialization in System.Text.Json
- CWE-502 — Deserialization of Untrusted Data
- FlawOpen — Is .NET BinaryFormatter safe?