FlawOpen

flawopen.com/Insecure Deserialization/TypeNameHandling

Newtonsoft.Json TypeNameHandling: safe configuration for untrusted JSON?

CWE-502: Deserialization of Untrusted DataReference page
Short answer

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.

VULNERABLE
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);
FIXED
// 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

Is TypeNameHandling.Auto safe for untrusted JSON?

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.

Does System.Text.Json support polymorphism safely?

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.

How should I audit a .NET application?

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

See also Insecure Deserialization: Risks and Remediation PHP unserialize() and Untrusted Input Python pickle.load() Security: Untrusted Data Risks Is Ruby YAML.load Safe? Psych Versions & Safe Loading .NET BinaryFormatter Security: Risks and Migration Java ObjectInputStream Security: Safer Deserialization