flawopen.com/不安全的反序列化/TypeNameHandling
Newtonsoft.Json TypeNameHandling:不可信 JSON 的安全配置
普通 JSON 描述数据,由代码选择 .NET 类型。启用 TypeNameHandling 后,$type 可能影响反序列化时的类型解析。不可信输入应使用 None。若需多态,应以严格的 ISerializationBinder 验证类型,或使用代码声明的封闭判别值映射。
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+).
开发者为何启用 TypeNameHandling
常见原因是多态数据:声明为 Shape 的集合可能需要保留为 Circle 或 Square。Json.NET 通过 $type 读写类型名。不可信输入中的元数据可能影响解析出的运行时类型;风险取决于允许类型及其反序列化行为。危险利用链还需要可达且有副作用的类型;仅有 JSON 并不足够。
信任边界应反向设计:由应用代码从经过验证的封闭判别值集合中选择子类型。System.Text.Json 可通过 [JsonDerivedType] 显式启用多态,但不会激活 JSON 中任意 CLR 名称。相关边界见 BinaryFormatter 指南。
自定义 ISerializationBinder 足够吗?
外部数据使用的 TypeNameHandling 不是 None 时,Json.NET 要求严格的 ISerializationBinder。仅将少量精确的应用自有类型名映射到预期类型,并拒绝其他类型。审查允许类型、转换器、构造函数、setter 和回调。binder 只能限制解析,不能让任意类型无害。若可控协议,优先使用 None 与显式分派。
常见问题
单独使用并不安全。Auto 可能在反序列化时读取 $type。外部数据应使用 TypeNameHandling.None,或严格 binder 仅允许已审查类型。
支持显式启用:判别值和派生类型在代码中声明,例如 .NET 7+ 的 [JsonDerivedType]。应用代码限定类型集合,而非接受内容中的任意 CLR 名称。
全局搜索 TypeNameHandling、SerializationBinder 和 JsonConvert.DeserializeObject。检查 ASP.NET 启动与共享设置,追踪外部 JSON 进入各配置的端点,并确认所有非 None 路径都有严格 binder。