C#(.NET)中Newtonsoft.Json不安全反序列化相关技术问询
问题背景
- 正在开发对接第三方「万能API(God API)」的接口,该API支持900+功能,接收通用
object类型参数 - 已在仓库首个提交中搭建最小示例(
TestRequests.http可用于Rider HTTP客户端或Postman) - 虽设置
TypeNameHandling.None,公司安全审核仍认为API契约含object类型存在OWASP 2017 A8不安全反序列化风险,要求全部使用严格类型 - 尝试用抽象基类时,.NET报错:
"Could not create an instance of type DeserializationDemo.BaseSetting. Type is an interface or abstract class and cannot be instantiated." - 改用自定义ModelBinder可运行,但参数会被反序列化为
Newtonsoft.Json.Linq.JObject,且发现DemoContract1也会被转成该类型,担心仍有安全风险 - 当前仅有的替代方案是为900+功能各创建独立的严格类型端点,希望得到两个问题的解答:
- 设置
TypeNameHandling.None是否不足以避免不安全反序列化? - 是否有替代Custom ModelBinder的方案,可严格限制允许类型且不会反序列化为JObject?
- 设置
解答
1. TypeNameHandling.None是否能避免不安全反序列化?
是的,在正常配置下,TypeNameHandling.None足以阻断绝大多数基于类型注入的不安全反序列化攻击。
OWASP A8的不安全反序列化风险核心是:攻击者通过恶意构造的序列化数据,注入带危险逻辑的类实例,让反序列化器创建该对象并执行恶意代码。而TypeNameHandling.None会让Newtonsoft.Json完全忽略序列化数据中的类型信息,仅按你指定的目标类型反序列化——哪怕攻击者在请求里塞了类型注入的payload,反序列化器也不会识别,只会尝试把数据转成目标类型,失败就直接抛出异常。
安全审核的顾虑可能是基于“object类型本身存在不确定性”的通用风险,但从技术实现上,只要你没开启类型自动识别(即TypeNameHandling设为None),且反序列化逻辑中没有动态推断类型,object参数的反序列化不会引入额外的不安全反序列化风险。
2. 替代Custom ModelBinder的方案:泛型+类型约束的强类型端点封装
不需要为900+功能逐个写独立端点,可以用泛型控制器+类型约束的方式,既保证严格类型,又避免重复代码,同时不会反序列化为JObject。
具体实现步骤:
- 定义所有功能对应的强类型参数模型,统一实现一个标记接口(或继承同一个非抽象基类):
public interface IApiSetting { } public class DemoContract1 : IApiSetting { public string Name { get; set; } public int Age { get; set; } } // 其他900+功能的参数类都实现IApiSetting
- 创建泛型控制器,通过泛型参数约束限制允许的类型:
[ApiController] [Route("api/[controller]")] public class GodApiController<T> : ControllerBase where T : IApiSetting, new() { private readonly IThirdPartyGodApi _thirdPartyApi; public GodApiController(IThirdPartyGodApi thirdPartyApi) { _thirdPartyApi = thirdPartyApi; } [HttpPost] public async Task<IActionResult> Post([FromBody] T setting) { if (!ModelState.IsValid) { return BadRequest(ModelState); } // 调用第三方API,直接传入强类型的setting var result = await _thirdPartyApi.CallAsync(setting); return Ok(result); } }
- 在Program.cs中通过反射批量注册所有泛型控制器的具体实现:
builder.Services.AddControllers() .AddNewtonsoftJson(options => { options.SerializerSettings.TypeNameHandling = TypeNameHandling.None; }); // 反射获取所有实现IApiSetting的类型,批量注册泛型控制器 var apiSettingTypes = Assembly.GetExecutingAssembly().GetTypes() .Where(t => typeof(IApiSetting).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract); foreach (var type in apiSettingTypes) { var controllerType = typeof(GodApiController<>).MakeGenericType(type); builder.Services.AddTransient(controllerType); }
方案优势
- 每个功能对应一个强类型控制器实例,但无需手动编写900个控制器,通过反射批量注册即可
- 反序列化直接针对具体的
T类型(如DemoContract1),不会转成JObject,完全符合安全审核的“严格类型”要求 - 可维护性强,新增功能仅需添加对应的
IApiSetting实现类即可
额外适配:第三方API需接收object参数
如果第三方API必须接收object类型,直接把强类型的T实例传入即可——任何强类型对象都可隐式转为object,该转换类型安全,不会引入反序列化风险。
内容的提问来源于stack exchange,提问作者Paul Nogas
相关产品推荐
相关产品推荐

