【问题标题】:Is there a high performance way to replace the BinaryFormatter in .NET5?有没有一种高性能的方法来替换 .NET5 中的 BinaryFormatter?
【发布时间】:2020-11-12 07:29:05
【问题描述】:

在 .NET5 之前,我们通过这些代码序列化/反序列化字节/对象:

    private static byte[] StructToBytes<T>(T t)
    {
        using (var ms = new MemoryStream())
        {
            var bf = new BinaryFormatter();
            bf.Serialize(ms, t);
            return ms.ToArray();
        }
    }

    private static T BytesToStruct<T>(byte[] bytes)
    {
        using (var memStream = new MemoryStream())
        {
            var binForm = new BinaryFormatter();
            memStream.Write(bytes, 0, bytes.Length);
            memStream.Seek(0, SeekOrigin.Begin);
            var obj = binForm.Deserialize(memStream);
            return (T)obj;
        }
    }

但出于安全原因,BinaryFormatter 将被删除:

https://docs.microsoft.com/zh-cn/dotnet/standard/serialization/binaryformatter-security-guide

那么有没有一些简单但高性能的方法来替代 BinaryFormatter?

【问题讨论】:

  • 替换做什么?这个类是非常有问题的,通常很少使用,因为它允许将类型和代码注入应用程序。有很多方法可以以二进制格式序列化对象。现在最常见的一种是 gRPC 的 protobuf。大数据还有其他格式,例如 Parquet、Avro、ORC。
  • There's a probably duplicate question from 2010 询问 BinaryFormatter 替代方案。得益于 gRPC,Protobuf 现在比 2010 年更加普及
  • 没有好的替代品。 XmlSerializer 太慢了,json 有时会出现继承问题。 Messagepack 完全不可靠。对于本地数据,没有什么比使用 binaryformatter 更快的了,而且它每次都能 100% 工作。
  • @juFo 相反,有一个很好的替代品,速度也快得多:Protocol Buffers

标签: binaryformatter .net-5


【解决方案1】:

在我最近从 .NET Core 3.1 迁移到 .NET 5 的项目中,我用 Protobuf-net 替换了我们的 BinarySerializer 代码:https://github.com/protobuf-net/protobuf-net

代码几乎完全相同,该项目在 GitHub 上(目前)有 2200 万次下载和 3.2k 颗星,享有很高的声誉。它非常快,并且没有 BinarySerializer 周围的安全包袱。

这是我的 byte[] 序列化类:

public static class Binary
{
    /// <summary>
    /// Convert an object to a Byte Array, using Protobuf.
    /// </summary>
    public static byte[] ObjectToByteArray(object obj)
    {
        if (obj == null)
            return null;

        using var stream = new MemoryStream();

        Serializer.Serialize(stream, obj);

        return stream.ToArray();
    }

    /// <summary>
    /// Convert a byte array to an Object of T, using Protobuf.
    /// </summary>
    public static T ByteArrayToObject<T>(byte[] arrBytes)
    {
        using var stream = new MemoryStream();

        // Ensure that our stream is at the beginning.
        stream.Write(arrBytes, 0, arrBytes.Length);
        stream.Seek(0, SeekOrigin.Begin);

        return Serializer.Deserialize<T>(stream);
    }
}

我确实必须为我序列化的类添加属性。它只用 [Serializable] 进行了装饰,虽然我知道 Protobuf 可以使用很多常见的装饰,但那个没有用。来自github上的例子:

[ProtoContract]
class Person {
    [ProtoMember(1)]
    public int Id {get;set;}
    [ProtoMember(2)]
    public string Name {get;set;}
    [ProtoMember(3)]
    public Address Address {get;set;}
}

[ProtoContract]
class Address {
    [ProtoMember(1)]
    public string Line1 {get;set;}
    [ProtoMember(2)]
    public string Line2 {get;set;}
}

在我的例子中,我在 Redis 中缓存东西,效果很好。

也可以在您的 .csproject 文件中重新启用它:

<PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
    <EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
</PropertyGroup>

...但这是个坏主意。 BinaryFormatter 对 .NET 的许多历史漏洞负责,并且无法修复。它可能会在未来的 .NET 版本中完全不可用,因此替换它是正确的举措。

【讨论】:

  • 不要因上述SO question 中的“安全”问题而气馁。 Microsoft 提出的BinaryFormatter 的替代品(XmlSerializerSystem.Text.JSON)同样容易受到窥探。所有这些都可以通过加密流来缓解。 BinaryFormatter 的漏洞要严重得多。格式错误的二进制数据源可能会导致任何事情,从崩溃到成为恶意软件的载体。
  • @NPras 这也是我的结论。重要的是要知道它不是开箱即用的安全工具,但它也不会直接受到无法解决的攻击。
【解决方案2】:

如果您使用的是 .NET Core 5 或更高版本,则可以像这样使用新的 System.Text.Json.JsonSerializer.Serialize 和 System.Text.Json.JsonSerializer.Deserialize:

public static class Binary
{
    /// <summary>
    /// Convert an object to a Byte Array.
    /// </summary>
    public static byte[] ObjectToByteArray(object objData)
    {
        if (objData == null)
            return default;

       return Encoding.UTF8.GetBytes(JsonSerializer.Serialize(objData, GetJsonSerializerOptions()));
    }

    /// <summary>
    /// Convert a byte array to an Object of T.
    /// </summary>
    public static T ByteArrayToObject<T>(byte[] byteArray)
    {
        if (byteArray == null || !byteArray.Any())          
            return default;
            
        return JsonSerializer.Deserialize<T>(byteArray, GetJsonSerializerOptions());
    }

    private static JsonSerializerOptions GetJsonSerializerOptions()
    {
        return new JsonSerializerOptions()
        {
            PropertyNamingPolicy = null,
            WriteIndented = true,
            AllowTrailingCommas = true,
            DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
        };
    }
}

【讨论】:

    【解决方案3】:

    在 .NET Core 5 中有一个使用它的选项:

    只需添加

    <EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
    

    像这样的项目:

    <PropertyGroup>
        <TargetFramework>net5.0</TargetFramework>
        <EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
    </PropertyGroup>
    

    我相信它会起作用。

    【讨论】:

    • 是的,我已经添加了它,但我认为这不是风险的最佳解决方案。
    • 这是最好的方法,直到可以更改代码以使用替代方法。
    • @Fair 这是最糟糕的方式并且有时间限制。 .NET 5.0 是一个“当前”版本,这意味着一旦 .NET 5 明年推出,它将不再受支持。不要指望这个不安全的开关会继续工作。 Protocol Buffers 是一个伟大的、跨平台的、广泛支持的替代方案
    • 存在安全问题,因此不推荐用于生产
    猜你喜欢
    • 2021-10-16
    • 1970-01-01
    • 1970-01-01
    • 2017-05-20
    • 1970-01-01
    • 2021-12-19
    • 2016-01-19
    • 2011-11-08
    • 1970-01-01
    相关资源
    最近更新 更多