做网站 是不是懂ps,响应式布局如何实现,宜良网站建设,台州免费建站多态反序列化是处理继承结构对象序列化的常见需求#xff0c;但不同 JSON 序列化库的实现机制差异会带来显著的安全风险。微软 CA2326 规则明确警示#xff1a;避免使用非安全的 JsonSerializerSettings 配置#xff08;如 Newtonsoft.Json 的 TypeNameHandling 非 None 值但不同 JSON 序列化库的实现机制差异会带来显著的安全风险。微软 CA2326 规则明确警示避免使用非安全的 JsonSerializerSettings 配置如 Newtonsoft.Json 的 TypeNameHandling 非 None 值否则可能引发类型注入攻击。本文将对比 Newtonsoft.Json 与 System.Text.Json 在多态反序列化中的实现差异重点分析安全性问题并通过代码实例验证两者的安全表现。多态反序列化的实现机制差异Newtonsoft.Json基于TypeNameHandling 的灵活设计Newtonsoft.Json 通过 TypeNameHandling 配置项控制是否在 JSON 中嵌入类型元数据。当设置 TypeNameHandling 支持多态时JSON 会携带 $type 字段包含类型的完全限定名和程序集信息反序列化时直接根据该字段实例化对应类型。这种设计虽然灵活支持多态但缺乏默认的类型校验机制攻击者可构造包含恶意类型的 JSON触发敏感类型实例化。System.Text.Json多态配置的安全设计System.Text.Json 默认不支持多态反序列化需通过 [JsonDerivedType] 特性或 DerivedTypes 显式声明允许的派生类型。反序列化时仅处理配置过的类型拒绝未授权的类型注入从机制上规避了安全风险。CA2326 规则的警示CA2326 规则的核心是禁止使用 TypeNameHandling 非 None 值的配置 —— 攻击者可利用 $type 字段构造恶意 JSON实例化如 ProcessStartInfo执行系统命令、FileStream读写文件等敏感类型引发远程代码执行或数据泄露。using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using System.Diagnostics; using System.Text.Json; using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; namespace NewtonsoftSecurityDemo { [JsonPolymorphic(TypeDiscriminatorPropertyName CustomerType)] [JsonDerivedType(typeof(PaymentCompletedEvent), PaymentCompletedEvent)] [JsonDerivedType(typeof(OrderCreatedEvent), OrderCreatedEvent)] public class TransactionEvent { public string EventId { get; set; } Guid.NewGuid().ToString(); public DateTime EventTime { get; set; } DateTime.Now; public string OrderId { get; set; } // 业务扩展字段攻击者利用的入口 public object ExtData { get; set; } } public class PaymentCompletedEvent : TransactionEvent { public decimal Amount { get; set; } public string PaymentMethod { get; set; } } public class OrderCreatedEvent : TransactionEvent { public string UserId { get; set; } public int ItemCount { get; set; } } // Newtonsoft.Json 安全绑定器演示白名单校验 public class EventSerializationBinder : ISerializationBinder { // 仅允许的安全类型白名单 private readonly HashSetstring _allowedTypes new() { NewtonsoftSecurityDemo.PaymentCompletedEvent, NewtonsoftSecurityDemo.OrderCreatedEvent, //System.Diagnostics.ProcessStartInfo }; public Type BindToType(string assemblyName, string typeName) { // 仅允许白名单内的类型 if (!_allowedTypes.Contains(typeName)) { throw new NotSupportedException($禁止反序列化未授权类型{typeName}); } return Type.GetType(${typeName}, {assemblyName}) ?? typeof(TransactionEvent); } public void BindToName(Type serializedType, out string? assemblyName, out string? typeName) { assemblyName serializedType.Assembly.FullName; typeName serializedType.FullName; } } class Program { static void Main(string[] args) { Console.WriteLine( Newtonsoft.Json 命令执行攻击演示 ); Newtonsoft_Attack_ProcessStartInfo(); Console.WriteLine(\n Newtonsoft.Json 文件读取攻击演示 ); Newtonsoft_Attack_FileStream(); Console.WriteLine(\n Newtonsoft.Json 启用 SerializationBinder安全防护演示 ); Newtonsoft_Secure_WithBinder(); Console.WriteLine(\n System.Text.Json 安全防护演示 ); SystemTextJson_Defense(); Console.ReadKey(); } /// summary /// 模拟注入ProcessStartInfo执行系统命令 /// /summary static void Newtonsoft_Attack_ProcessStartInfo() { string maliciousCallbackJson $ {{ $type: NewtonsoftSecurityDemo.PaymentCompletedEvent, NewtonsoftSecurityDemo, EventId: {Guid.NewGuid()}, OrderId: ORD_{new Random().Next(1000, 9999)}, Amount: 999.00, PaymentMethod: Alipay, ExtData: {{ $type: System.Diagnostics.ProcessStartInfo,System.Diagnostics.Process, FileName: cmd.exe, Arguments: /c echo some scripts C:\temp\attack_log.txt echo doing C:\temp\attack_log.txt, UseShellExecute: true }} }}; var settings new JsonSerializerSettings { TypeNameHandling TypeNameHandling.Auto, }; var eventData Newtonsoft.Json.JsonConvert.DeserializeObjectTransactionEvent(maliciousCallbackJson, settings); Console.WriteLine($处理订单事件{eventData.OrderId}); if (eventData.ExtData is ProcessStartInfo psi) { Directory.CreateDirectory(C:\temp); Process.Start(psi); Console.WriteLine($ [攻击成功] 执行命令{psi.Arguments}); Console.WriteLine($ [攻击结果] 生成文件C:\temp\attack_log.txt 文件内容); if (File.Exists(C:\temp\attack_log.txt)) { string content File.ReadAllText(C:\temp\attack_log.txt); Console.WriteLine(${content}); } } } /// summary /// 模拟注入FileInfo读取敏感文件 /// /summary static void Newtonsoft_Attack_FileStream() { string targetFile Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), appsettings.json); if (!File.Exists(targetFile)) { File.WriteAllText(targetFile, ConnectionString: 123456); } string maliciousExportJson $ {{ $type: NewtonsoftSecurityDemo.OrderCreatedEvent, NewtonsoftSecurityDemo, OrderId: ORD_{new Random().Next(1000, 9999)}, UserId: user_{new Random().Next(100, 999)}, ExtData: {{ $type: System.IO.FileInfo, FileName: {targetFile.Replace(\, \\)} }} }}; var settings new JsonSerializerSettings { TypeNameHandling TypeNameHandling.Auto }; var eventData Newtonsoft.Json.JsonConvert.DeserializeObjectTransactionEvent(maliciousExportJson, settings); Console.WriteLine($处理订单导出{eventData.OrderId}); // 通过FileInfo读取文件内容模拟攻击逻辑 if (eventData.ExtData is FileInfo fileInfo) { using (var sr new StreamReader(fileInfo.OpenRead())) { string sensitiveContent sr.ReadToEnd(); Console.WriteLine($ [攻击成功] 读取敏感文件内容\n{sensitiveContent}); } } } /// summary /// Newtonsoft.Json 启用SerializationBinder拦截恶意类型 /// /summary static void Newtonsoft_Secure_WithBinder() { string maliciousCallbackJson $ {{ $type: NewtonsoftSecurityDemo.PaymentCompletedEvent, NewtonsoftSecurityDemo, EventId: {Guid.NewGuid()}, OrderId: ORD_{new Random().Next(1000, 9999)}, Amount: 999.00, PaymentMethod: Alipay, ExtData: {{ $type: System.Diagnostics.ProcessStartInfo,System.Diagnostics.Process, FileName: cmd.exe, Arguments: /c echo some scripts C:\temp\attack_log.txt echo doing C:\temp\attack_log.txt, UseShellExecute: true }} }}; var settings new JsonSerializerSettings { TypeNameHandling TypeNameHandling.Auto, SerializationBinder new EventSerializationBinder() // 启用白名单校验 }; try { var eventData Newtonsoft.Json.JsonConvert.DeserializeObjectTransactionEvent(maliciousCallbackJson, settings); if (eventData.ExtData is ProcessStartInfo) { Console.WriteLine( [防护失效] 恶意类型未被拦截异常); } } catch (Exception ex) { Console.WriteLine($ [防护成功] 拦截未授权类型{ex.Message}); } } /// summary /// System.Text.Json 安全防护验证 /// /summary static void SystemTextJson_Defense() { string maliciousCallbackJson $ {{ CustomerType: PaymentCompletedEvent, EventId: {Guid.NewGuid()}, OrderId: ORD_{new Random().Next(1000, 9999)}, Amount: 999.00, PaymentMethod: Alipay, ExtData: {{ $type: System.Diagnostics.ProcessStartInfo,System.Diagnostics.Process, FileName: cmd.exe, Arguments: /c echo some scripts C:\temp\attack_log.txt echo doing C:\temp\attack_log.txt, UseShellExecute: true }} }}; var eventData System.Text.Json.JsonSerializer.DeserializeTransactionEvent(maliciousCallbackJson); Console.WriteLine($ 主对象类型{eventData.GetType().FullName}); Console.WriteLine($ ExtData 实际类型{eventData.ExtData.GetType().FullName}); if (eventData.ExtData is JsonElement) { Console.WriteLine( [防护成功] 恶意类型ProcessStartInfo被拦截ExtData仅保留原始JSON结构未反序列化为恶意对象); } else if (eventData.ExtData is ProcessStartInfo) { Console.WriteLine( [防护失效] 恶意类型解析成功); } else { Console.WriteLine($ [正常业务] 解析到合法类型{eventData.ExtData.GetType().FullName}); } Console.WriteLine(\n尝试转换ExtData为ProcessStartInfo); try { var psi (ProcessStartInfo)eventData.ExtData; Console.WriteLine( [防护失效] 恶意类型解析成功); } catch (InvalidCastException ex) { Console.WriteLine($ [防护成功] 强制转换失败原因{ex.Message}); } } } }运行结果为通过 Demo 可以发现- Newtonsoft 无防护时攻击成功- Newtonsoft 启用 SerializationBinder 后拦截了恶意类型- System.Text.Json 始终拦截恶意类型ExtData 为 JsonElement无法转换为 ProcessStartInfo。为什么 Newtonsoft.Json 启用 SerializationBinder 可降低风险先看代码public class EventSerializationBinder : ISerializationBinder { // 仅允许的安全类型白名单 private readonly HashSetstring _allowedTypes new() { NewtonsoftSecurityDemo.PaymentCompletedEvent, NewtonsoftSecurityDemo.OrderCreatedEvent, //System.Diagnostics.ProcessStartInfo }; public Type BindToType(string assemblyName, string typeName) { // 仅允许白名单内的类型 if (!_allowedTypes.Contains(typeName)) { throw new NotSupportedException($禁止反序列化未授权类型{typeName}); } return Type.GetType(${typeName}, {assemblyName}) ?? typeof(TransactionEvent); } public void BindToName(Type serializedType, out string? assemblyName, out string? typeName) { assemblyName serializedType.Assembly.FullName; typeName serializedType.FullName; } }SerializationBinder 的核心作用是接管从 JSON 中的 $type 字符串 到实际 Type 类型的映射过程强制校验类型合法性。简单说- 无 SerializationBinder反序列化器会无条件反射创建 $type 指定的任意类型包括危险类型- 有 SerializationBinder反序列化器必须经过你的自定义校验逻辑仅允许白名单内的类型被实例化直接阻断恶意类型的创建。小结Newtonsoft.Json 的 TypeNameHandling 机制虽灵活但易被利用触发安全漏洞System.Text.Json 通过显式多态配置白名单的设计规避了类型注入风险。在实际开发中针对多态场景建议优先使用 System.Text.Json。若必须使用 Newtonsoft.Json需遵循以下安全实践- 避免使用 TypeNameHandling 非 None 值。- 若必须启用需严格校验 $type 字段类型的合法性仅允许安全类型。我希望您喜欢这篇文章并一如既往地感谢您阅读并与朋友和同事分享我的文章。