FlawOpen

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

.NET 的 BinaryFormatter 安全吗?

CWE-502: Deserialization of Untrusted Data参考指南
简短回答:不存在安全配置

Microsoft 指出 BinaryFormatter 不安全且无法变安全,并建议停止使用,即使应用认为输入可信也一样。在 .NET 9 中,API 类型仍然存在,但内置实现每次调用都会抛出 PlatformNotSupportedException;旧版运行时和不受支持的兼容包仍保留原有风险。

不安全
// 不要反序列化跨越信任边界的数据。
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

public static class LegacyReader
{
    public static object Deserialize(Stream input) =>
        new BinaryFormatter().Deserialize(input);
}

// 同样应移除这些无限制多态序列化器:
SoapFormatter
NetDataContractSerializer
LosFormatter
ObjectStateFormatter  // ASP.NET ViewState 内部使用

// SerializationBinder 不能让这些格式变安全。
迁移
// 数据形状由应用代码声明。
// Nullable reference types make the DTO contract explicit.
#nullable enable
using System;
using System.IO;
using System.Text.Json;

public sealed record MyDto(int Id, string Name);

public static class DtoReader
{
    public static MyDto Parse(ReadOnlySpan<byte> json)
    {
        // 限制输入大小并验证应用规则。
        if (json.Length > 65_536)
            throw new InvalidDataException();

        MyDto? value = JsonSerializer.Deserialize<MyDto>(json);
        if (value is null || value.Id <= 0 ||
            string.IsNullOrWhiteSpace(value.Name) ||
            value.Name.Length > 100)
            throw new InvalidDataException();

        return value;
    }
}

// 对旧 NRBF 文件用 NrbfDecoder 检查
// 记录,再将允许的值映射到 DTO。
// 不要重建任意运行时类型。

BinaryFormatter 为什么危险

BinaryFormatter.Deserialize 会从数据流重建对象图,攻击者控制的数据可能影响程序控制流。Microsoft 警告其可能造成拒绝服务、信息泄露和远程代码执行。核心问题是无限制的多态反序列化:序列化数据可以影响程序处理哪些类型和对象状态。没有任何 formatter 开关能把它变成安全的不可信字节解析器。

风险并不局限于公开 HTTP 接口。文件、缓存、队列、数据库记录、桌面客户端消息和云同步状态以后都可能受攻击者影响。追踪字节如何进入并经过系统;“数据是我们写的”或“数据是内部的”不能作为永久信任标签。

SerializationBinder 为什么不够

自定义 SerializationBinder 可以限制 formatter 尝试解析的类型,但 Microsoft 明确指出,binder 或其他配置开关无法缓解拒绝服务攻击,对其他攻击类型也可能不足。Binder 不能替代迁移;在移除 BinaryFormatter 之前,不要接受不可信输入。

查找遗留调用与相关路径

在源代码、项目引用和运行时配置中搜索 BinaryFormatter、IFormatter、SoapFormatter、NetDataContractSerializer、LosFormatter 和 ObjectStateFormatter。检查会话状态提供程序、缓存适配器、队列消费者、文件导入导出和旧 ASP.NET Web Forms 路径。ViewState 通常由消息认证码(MAC)保护;保持该保护开启并保护 machine key。MAC 并不能让通用 BinaryFormatter 反序列化变安全。

按数据格式和运行时版本制定迁移计划

新数据应根据格式和兼容需求选择显式契约,例如 System.Text.Json、DataContractSerializer、MessagePack 或 protobuf-net;没有直接替代品。若必须保留旧 NRBF 文件,.NET 9 的 NrbfDecoder 可在不实例化编码应用类型的情况下解码记录。验证解码后的结构,并只将预期值映射到应用 DTO。另行查看 WinForms/WPF 对资源、剪贴板和拖放场景的迁移指南。

常见问题

我们只反序列化自己写入的数据,这样可以吗?

Microsoft 建议即使数据看似可信也迁移,因为存储、缓存、文件、同步和下游服务都可能跨越信任边界。查明谁能写入每个来源,并停止使用 BinaryFormatter,不要依赖信任假设。

升级到 .NET 9 后会怎样?

在 .NET 9 中,API 类型仍存在,但内置实现始终抛出 PlatformNotSupportedException,旧兼容开关也无效。升级前审计调用并完成迁移。不受支持的兼容包会恢复有风险的实现,并非安全修复。

Json.NET 是安全替代方案吗?

Json.NET 默认将 TypeNameHandling 设为 None,不会让 payload 元数据选择任意 CLR 类型。不要对不可信输入启用其他值。若需要多态,应使用应用控制的严格允许列表并遵循库的安全指南。参见TypeNameHandling 安全吗?

来源与参考资料

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