是否可以使用可信类型标记来防止 Json.Net TypeNameHandling 漏洞?
Is it possible to prevent the Json.Net TypeNameHandling vulnerability with a marker for trusted types?
我正在阅读 vulnerability of deserializing types with Json.Net using a setting different from TypeNameHandling.None
. The Json.Net docs recommend implementing a custom SerializationBinder
. A simple example of a custom binder that checks types against a list of known types is given here。
虽然此解决方案确实有效,但已知类型集在我的场景中并不固定,因为应用程序必须支持扩展,这可能会定义自己的数据 类。一种解决方案是在注册扩展期间扩展已知类型列表,但是,我想到了第二种方法,我想验证一下:
我想为可信类型定义一个通用接口:
(dbc 建议:可以使用自定义属性代替标记界面。)
public interface ITrustedType { }
然后,任何需要由应用程序或扩展序列化(反)序列化的类型都可以实现此接口:
public class Car : ITrustedType
{
public IList<Passenger> Passengers { get; set; }
// ...
}
public class Passenger : ITrustedType
{
// ...
}
自定义绑定器不检查类型列表,而是确保每个(反)序列化类型都实现了受信任的类型接口,如果实现则应用默认行为,如果失败则应用默认行为它没有。
在可枚举类型的情况下,它将确保泛型类型参数是受信任的类型。
到目前为止,这是我测试过的一个简单实现:
public class TrustedTypesBinder : DefaultSerializationBinder
{
static bool IsCollectionType(Type type)
{
return type.GetInterfaces().Any(s => s.Namespace == "System.Collections.Generic" && s.Name.StartsWith("IEnumerable`"));
}
static public bool IsTrustedType(Type type)
{
if (IsCollectionType(type))
{
return type.GenericTypeArguments.All(t => IsTrustedType(t));
}
return typeof(ITrustedType).IsAssignableFrom(type);
}
public override Type BindToType(string assemblyName, string typeName)
{
Type type = base.BindToType(assemblyName, typeName);
if (!IsTrustedType(type))
throw new JsonSerializationException(typeName + " is not a trusted type.");
return type;
}
public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
if (!IsTrustedType(serializedType))
throw new JsonSerializationException(serializedType.FullName + " is not a trusted type.");
base.BindToName(serializedType, out assemblyName, out typeName);
}
}
我怀疑此解决方案是安全的,因为来自应用程序或扩展程序集外部的任何类型都不会实现此接口,因此无法从恶意 JSON 输入构造攻击工具。
我是否遗漏了任何攻击向量?您知道可能会受到危害的方式吗?
编辑:
我知道,任何人都可以实现这个 ITrustedType
接口并赋予它一些任意行为。据我所知,这在运行时的(反)序列化上下文中不应该是相关的。应用程序本身以及扩展都来自可信来源,因此,实现的类型当然是受控的,攻击者不可能将任何新类型添加到已发布的应用程序二进制文件中。
这个问题专门针对在运行时出现的实例化类型,这些类型可能会被用于恶意目的。
编辑 2:
根据 Ben Voigts 的建议,将 ICollection 更改为 IEnumerable。
当你遇到一个没有标记的类型时,检查它的泛型类型参数是不够的,你需要检查每个 public 属性 的类型和每个参数的类型public 构造函数,因为这些是序列化足迹。
例如,您确实不想允许对 System.Data.TypedTableBase<T>
进行反序列化,即使 T
是安全的,因为它具有允许配置数据库访问的 public 属性。
我正在阅读 vulnerability of deserializing types with Json.Net using a setting different from TypeNameHandling.None
. The Json.Net docs recommend implementing a custom SerializationBinder
. A simple example of a custom binder that checks types against a list of known types is given here。
虽然此解决方案确实有效,但已知类型集在我的场景中并不固定,因为应用程序必须支持扩展,这可能会定义自己的数据 类。一种解决方案是在注册扩展期间扩展已知类型列表,但是,我想到了第二种方法,我想验证一下:
我想为可信类型定义一个通用接口:
(dbc 建议:可以使用自定义属性代替标记界面。)
public interface ITrustedType { }
然后,任何需要由应用程序或扩展序列化(反)序列化的类型都可以实现此接口:
public class Car : ITrustedType
{
public IList<Passenger> Passengers { get; set; }
// ...
}
public class Passenger : ITrustedType
{
// ...
}
自定义绑定器不检查类型列表,而是确保每个(反)序列化类型都实现了受信任的类型接口,如果实现则应用默认行为,如果失败则应用默认行为它没有。
在可枚举类型的情况下,它将确保泛型类型参数是受信任的类型。
到目前为止,这是我测试过的一个简单实现:
public class TrustedTypesBinder : DefaultSerializationBinder
{
static bool IsCollectionType(Type type)
{
return type.GetInterfaces().Any(s => s.Namespace == "System.Collections.Generic" && s.Name.StartsWith("IEnumerable`"));
}
static public bool IsTrustedType(Type type)
{
if (IsCollectionType(type))
{
return type.GenericTypeArguments.All(t => IsTrustedType(t));
}
return typeof(ITrustedType).IsAssignableFrom(type);
}
public override Type BindToType(string assemblyName, string typeName)
{
Type type = base.BindToType(assemblyName, typeName);
if (!IsTrustedType(type))
throw new JsonSerializationException(typeName + " is not a trusted type.");
return type;
}
public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
if (!IsTrustedType(serializedType))
throw new JsonSerializationException(serializedType.FullName + " is not a trusted type.");
base.BindToName(serializedType, out assemblyName, out typeName);
}
}
我怀疑此解决方案是安全的,因为来自应用程序或扩展程序集外部的任何类型都不会实现此接口,因此无法从恶意 JSON 输入构造攻击工具。
我是否遗漏了任何攻击向量?您知道可能会受到危害的方式吗?
编辑:
我知道,任何人都可以实现这个 ITrustedType
接口并赋予它一些任意行为。据我所知,这在运行时的(反)序列化上下文中不应该是相关的。应用程序本身以及扩展都来自可信来源,因此,实现的类型当然是受控的,攻击者不可能将任何新类型添加到已发布的应用程序二进制文件中。
这个问题专门针对在运行时出现的实例化类型,这些类型可能会被用于恶意目的。
编辑 2: 根据 Ben Voigts 的建议,将 ICollection 更改为 IEnumerable。
当你遇到一个没有标记的类型时,检查它的泛型类型参数是不够的,你需要检查每个 public 属性 的类型和每个参数的类型public 构造函数,因为这些是序列化足迹。
例如,您确实不想允许对 System.Data.TypedTableBase<T>
进行反序列化,即使 T
是安全的,因为它具有允许配置数据库访问的 public 属性。