C# Newtonsoft TypeNameHandling.all具有基本命名空间检查是否安全?

C# Newtonsoft TypeNameHandling.all具有基本命名空间检查是否安全?,c#,security,interface,json.net,json-deserialization,C#,Security,Interface,Json.net,Json Deserialization,在我们的API上,我们需要接收json,将其反序列化为接口,设置一个字段,然后将其发送出去。为了实现这一点,我将两端的jsonConvert设置为使用TypeNameHandling.All。所讨论的端点应该被完全锁定,但总有可能有人获得访问权限并使用危险的构造函数或垃圾收集方法将$type设置为系统类 我的问题是,在尝试反序列化之前澄清类型的名称空间是否足够安全?或者json中仍然存在类似子对象的危险类类型的风险吗?如果仍然存在我错过的风险或漏洞,我还可以采取哪些其他步骤来减轻风险 我们的公司

在我们的API上,我们需要接收json,将其反序列化为接口,设置一个字段,然后将其发送出去。为了实现这一点,我将两端的jsonConvert设置为使用TypeNameHandling.All。所讨论的端点应该被完全锁定,但总有可能有人获得访问权限并使用危险的构造函数或垃圾收集方法将$type设置为系统类

我的问题是,在尝试反序列化之前澄清类型的名称空间是否足够安全?或者json中仍然存在类似子对象的危险类类型的风险吗?如果仍然存在我错过的风险或漏洞,我还可以采取哪些其他步骤来减轻风险

我们的公司名称位于我们使用的每个名称空间的开头,因此在下面的代码中,我们只需检查json中设置的类型是否以我们的公司名称开头。开始时的{}只是为了让编译器知道在检查之后不需要将JObject保存在内存中

{ //check the type is valid
    var securityType = JsonConvert.DeserializeObject<JObject>(request.requestJson);
    JToken type;
    if (securityType.TryGetValue("$type", out type))
    {
        if (!type.ToString().ToLower().StartsWith("foo")) { //'foo' is our company name, all our namespaces start with foo
            await logError($"Possible security violation, client tried  to instantiate {type}", clientId: ClientId);
            throw new Exception($"Request type {type} not supported, please use an IFoo");
        }
    }
    else
    {
        throw new Exception("set a type...");
    }
}
IFoo requestObject = JsonConvert.DeserializeObject<IFoo>(request.requestJson, new JsonSerializerSettings()
{
    TypeNameHandling = TypeNameHandling.All
});
{//检查类型是否有效
var securityType=JsonConvert.DeserializeObject(request.requestJson);
JToken型;
if(securityType.TryGetValue($type,out-type))
{
如果(!type.ToString().ToLower().StartsWith(“foo”){/'foo'是我们的公司名称,那么我们所有的名称空间都以foo开头
wait logError($“可能的安全冲突,客户端试图实例化{type}”,clientId:clientId);
抛出新异常($“不支持请求类型{type},请使用IFoo”);
}
}
其他的
{
抛出新异常(“设置类型…”);
}
}
IFoo requestObject=JsonConvert.DeserializeObject(request.requestJson,新的JsonSerializerSettings()
{
TypeNameHandling=TypeNameHandling.All
});

TypeNameHandling的风险在于,攻击者可能会诱骗接收者构建一个攻击小工具——一个在构建、填充或处置时会对接收系统造成攻击的类型实例。有关概述,请参阅

如果您要通过要求所有反序列化类型位于您自己公司的.Net命名空间中来防范此类攻击,请注意,当使用
TypeNameHandling.all进行序列化时,
“$type”
信息将出现在整个JSON令牌层次结构中,适用于所有数组和对象(包括.Net类型,如
列表
。因此,您必须应用
“$type”
,检查可能出现的所有类型信息。最简单的方法是使用以下方法:

public class MySerializationBinder : DefaultSerializationBinder
{
    const string MyNamespace = "foo"; //'foo' is our company name, all our namespaces start with foo

    public override Type BindToType(string assemblyName, string typeName)
    {
        if (!typeName.StartsWith(MyNamespace, StringComparison.OrdinalIgnoreCase))
            throw new JsonSerializationException($"Request type {typeName} not supported, please use an IFoo");
        var type = base.BindToType(assemblyName, typeName);
        return type;
    }
}
可按如下方式使用:

var settings = new JsonSerializerSettings
{
    SerializationBinder = new MySerializationBinder(),
    TypeNameHandling = TypeNameHandling.All,
};
由于不再需要将预加载到
JObject
中,因此这具有比您的解决方案更高性能的额外优势

但是,这样做之后,您可能会遇到以下问题:

  • 即使根对象始终来自您公司的命名空间,
    “$type”
    嵌套值的属性不一定在您公司的命名空间中。具体而言,将包括无害的通用系统集合(如
    列表
    字典
    以及数组)的类型信息。您可能需要增强以将此类类型列入白名单

    使用
    typenameholling.Objects
    typenameholling.Auto
    序列化可以减少将此类无害系统类型列入白名单的需要,因为与
    typenameholling.All相比,此类系统类型的类型信息在序列化过程中不太可能被包括在内

    <> P> <强>为了进一步简化类型检查以及减少攻击面的总体< /强>,您可能只考虑根对象上的类型信息。要做到这一点,请参见。e有关非根对象的信息

    或者,您可以使用
    TypeNameHandling进行序列化和反序列化。无
    全局性,并将根对象包装在标有like so的容器中:

    公共类根目录
    {
    [JsonProperty(TypeNameHandling=TypeNameHandling.Auto)]
    公共数据库数据{get;set;}
    }
    
    由于您的根对象似乎都实现了某个接口
    IFoo
    ,因此您将序列化和反序列化
    root
    ,这将把可能的攻击小工具的空间限制在实现
    IFoo
    的类上——一个更小的攻击面

    演示小提琴

  • 反序列化泛型时,外部泛型和内部泛型参数类型可能都需要递归清理。例如,如果您的命名空间包含
    泛型
    ,则检查typeName是否以您公司的命名空间开头将无法防止通过
    泛型
    的攻击

  • 即使只允许来自您自己的命名空间的类型,也很难说这足够安全,因为我们不知道您自己的命名空间中的任何类是否会被重新用作攻击小工具


  • 您只清理根对象上的
    $type
    ,而不是任何嵌套对象。要清理整个层次结构中的类型,您需要一个。使用自定义绑定器会更高效,因为您不需要预加载到
    作业对象中。
    。假设您使用自定义序列化绑定器清理
    $type
    ,很难说这足够安全,因为我们不知道您自己的命名空间中的任何类是否会被重新用作攻击小工具。有关一些背景资料,请参阅和。
    public class Root<TBase>
    {
        [JsonProperty(TypeNameHandling = TypeNameHandling.Auto)]
        public TBase Data { get; set; }
    }