binaryformatter在.net 5+中被弃用,因其反序列化机制存在严重安全风险,可能被利用执行远程代码;2. 使用它时必须确保类标记[serializable],通过流进行序列化与反序列化操作,并可借助[nonserialized]控制字段;3. 其主要风险在于反序列化不可信数据时可能触发恶意类型实例化,形成反序列化漏洞;4. 推荐替代方案包括system.text.json、newtonsoft.json、protobuf和messagepack,它们更安全高效;5. 仅在遗留系统或完全可信环境中才应考虑使用binaryformatter;6. 若必须使用,应通过serializationbinder限制类型、校验数据完整性、遵循最小权限原则并尽快迁移到现代方案,以降低风险,但根本解决之道是停止使用。
C#中的
BinaryFormatter
在实际操作中,使用
BinaryFormatter
BinaryFormatter
将C#对象通过
BinaryFormatter
1. 标记可序列化类型: 你的类必须使用
[Serializable]
BinaryFormatter
[Serializable] public class MyDataObject { public int Id { get; set; } public string Name { get; set; } public DateTime CreatedDate { get; set; } // 这个字段不会被序列化 [NonSerialized] public string TempInfo; public MyDataObject(int id, string name) { Id = id; Name = name; CreatedDate = DateTime.Now; TempInfo = "这是一个临时信息"; } public override string ToString() { return $"Id: {Id}, Name: {Name}, Created: {CreatedDate}, TempInfo (NonSerialized): {TempInfo}"; } }
2. 序列化对象: 你需要一个
BinaryFormatter
Stream
FileStream
MemoryStream
using System.IO; using System.Runtime.Serialization.Formatters.Binary; using System.Runtime.Serialization; // For ISerializable if needed // 创建一个要序列化的对象实例 MyDataObject originalObject = new MyDataObject(101, "示例数据"); Console.WriteLine($"原始对象: {originalObject}"); // 序列化到文件 string filePath = "mydata.bin"; try { using (FileStream fs = new FileStream(filePath, FileMode.Create)) { BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(fs, originalObject); } Console.WriteLine($"对象已序列化到 {filePath}"); } catch (SerializationException e) { Console.WriteLine($"序列化失败: {e.Message}"); } catch (Exception e) { Console.WriteLine($"发生未知错误: {e.Message}"); }
3. 反序列化对象: 反序列化是将字节流重新转换回对象的过程。同样需要一个
BinaryFormatter
Stream
MyDataObject deserializedObject = null; try { using (FileStream fs = new FileStream(filePath, FileMode.Open)) { BinaryFormatter formatter = new BinaryFormatter(); // 关键点:反序列化时,你必须非常信任数据的来源! deserializedObject = (MyDataObject)formatter.Deserialize(fs); } Console.WriteLine($"对象已从 {filePath} 反序列化成功。"); Console.WriteLine($"反序列化对象: {deserializedObject}"); // 注意:TempInfo 字段因为 [NonSerialized] 而不会被保留其值,会是其默认值(null) // 这就是为什么原始对象的 TempInfo 有值,而反序列化后会是 null if (deserializedObject.TempInfo == null) { Console.WriteLine("注意: TempInfo 字段因为被标记为 [NonSerialized] 而没有被序列化/反序列化。"); } } catch (SerializationException e) { Console.WriteLine($"反序列化失败: {e.Message}"); } catch (Exception e) { Console.WriteLine($"发生未知错误: {e.Message}"); }
4. 控制序列化行为(可选): 如果你需要更精细地控制序列化过程,例如在序列化时排除特定字段,或者在反序列化时进行一些自定义初始化,可以实现
ISerializable
GetObjectData
BinaryFormatter
BinaryFormatter
BinaryFormatter
BinaryFormatter
这被称为“反序列化漏洞”,它是一个臭名昭著的远程代码执行(RCE)向量。想象一下,你的应用程序从网络接收到一段数据,然后天真地使用
BinaryFormatter
BinaryFormatter
[Serializable]
BinaryFormatter
鉴于
BinaryFormatter
System.Text.Json
Newtonsoft.Json (Json.NET):作为事实上的.NET JSON序列化标准,
Newtonsoft.Json
System.Text.Json
System.Text.Json
Newtonsoft.Json
Google Protocol Buffers (Protobuf):如果你的应用对性能和数据包大小有极高要求,并且需要跨语言通信,Protobuf是一个非常优秀的二进制序列化方案。它通过定义
.proto
MessagePack for C#:类似于Protobuf,MessagePack也是一种高效的二进制序列化格式。它以其极快的序列化/反序列化速度和紧凑的数据格式而闻名,并且支持Schema-less(无模式)序列化,这在某些场景下提供了更大的灵活性。
System.Xml.Serialization.XmlSerializer
System.Runtime.Serialization.DataContractSerializer
XmlSerializer
DataContractSerializer
选择哪种方案,取决于你的具体需求:是需要人类可读性、跨平台兼容性、极致性能,还是与其他系统互操作。
BinaryFormatter
尽管
BinaryFormatter
遗留系统集成:你正在维护或与一个历史悠久的.NET应用程序交互,该程序在设计之初就大量依赖
BinaryFormatter
受控且高度信任的环境:在极少数情况下,如果你的应用程序运行在一个完全隔离、数据来源绝对可信的环境中(例如,仅用于应用程序内部的、进程内的数据传递,且数据完全由你自己的代码生成和消费,没有任何外部输入),并且你完全了解其风险,理论上可以使用。但这仍然不推荐,因为即使是内部数据,也可能因程序漏洞导致数据被篡改。
如何降低潜在风险(如果必须使用):
请注意,以下措施并不能完全消除
BinaryFormatter
限制反序列化的类型(SerializationBinder
SerializationBinder
BindToType
public sealed class WhitelistSerializationBinder : SerializationBinder { public override Type BindToType(string assemblyName, string typeName) { // 严格限制只允许反序列化 MyDataObject 类型 // 注意:这里需要完整的类型名称,包括命名空间和程序集信息 if (typeName == "YourNamespace.MyDataObject" && assemblyName.StartsWith("YourAssemblyName,")) { return Type.GetType($"{typeName}, {assemblyName}"); } // 阻止反序列化其他任何类型 throw new SerializationException($"不允许反序列化类型: {typeName}, {assemblyName}"); } } // 使用时: // formatter.Binder = new WhitelistSerializationBinder(); // deserializedObject = (MyDataObject)formatter.Deserialize(fs);
这个方法可以有效阻止恶意类型被实例化,但它要求你对所有可能被序列化的类型有清晰的白名单。
数据完整性校验:在反序列化之前,对数据流进行完整性校验,例如通过计算哈希值并与已知安全哈希值进行比对。如果哈希值不匹配,则拒绝反序列化。这可以防止数据在传输或存储过程中被篡改,但无法阻止攻击者构造新的恶意负载。
最小权限原则:运行序列化/反序列化代码的进程,应以尽可能低的权限运行,限制其对文件系统、网络或其他资源的访问能力。即使发生RCE,也能限制其造成的损害。
隔离处理:如果可能,将
BinaryFormatter
尽快迁移:以上所有措施都是权宜之计。长远来看,最根本的解决方案是逐步将所有使用
BinaryFormatter
System.Text.Json
记住,安全是一个持续的过程,而不是一劳永逸的解决方案。对于像
BinaryFormatter
以上就是C#的BinaryFormatter如何序列化对象?的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 //m.sbmmt.com/ All Rights Reserved | php.cn | 湘ICP备2023035733号