FlawOpen

flawopen.com/不安全的反序列化/TypeNameHandling

Newtonsoft.Json TypeNameHandling:不可信 JSON 的安全配置

CWE-502: Deserialization of Untrusted Data参考页面
简要回答

普通 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 与显式分派。

常见问题

TypeNameHandling.Auto 对不可信 JSON 安全吗?

单独使用并不安全。Auto 可能在反序列化时读取 $type。外部数据应使用 TypeNameHandling.None,或严格 binder 仅允许已审查类型。

System.Text.Json 支持安全多态吗?

支持显式启用:判别值和派生类型在代码中声明,例如 .NET 7+ 的 [JsonDerivedType]。应用代码限定类型集合,而非接受内容中的任意 CLR 名称。

如何审计 .NET 应用?

全局搜索 TypeNameHandling、SerializationBinder 和 JsonConvert.DeserializeObject。检查 ASP.NET 启动与共享设置,追踪外部 JSON 进入各配置的端点,并确认所有非 None 路径都有严格 binder。

参考资料

另请参阅 不安全反序列化:风险与修复 | flawopen.com PHP unserialize() 与不可信数据 Python pickle.load():不可信数据的安全风险 Ruby YAML.load 安全吗?Psych 版本与 safe_load .NET BinaryFormatter:风险与迁移 Java ObjectInputStream 安全:降低反序列化风险