初探Jackson白名单机制

  • 1.PolymorphicTypeValidator
  • 1.1简介
  • 1.2 方法
  • 1.3使用
  • 2.白名单的基本实现


1.PolymorphicTypeValidator

1.1简介

一个关键的类:PolymorphicTypeValidator。该抽象类提供了一些方法用于判断基类及其子类的有效性来决定是否允许Jackson反序列化,所有的这些方法的实现都必须是线程安全的以及可共享的,并且结果缓存在每一个属性上,通常是root级别的,而不是validator级别的。同时缓存也要保证线程安全,因为ObjectMapper和其他可访问的mappers是要共享使用的。

接下来看一下PolymorphicTypeValidator类的内容:一个枚举属性Validity,包含三个值,分别为ALLOWED,DENIED,INDETERMINATE。ALLOWED表明相关类名或者class代表的类可以被使用不需要进一步的检查。DENIED表示相关类名或者class代表的类不被允许使用而且没有必要再进一步的检查。INDETERMINATE表明相关类名或class代表的类的有效性不能被validator确定,需要进一步的检查。

此外该类中提供了一个自身实现的静态公共内部类,Base。官方建议都基于它来实现用户自己的实现,该内部类中所有的实现都返回了INDETERMINATE,所以用户只需要关心自己想要实现的方法。官方给了一个LaissezFaireSubTypeValidator的Base的实现类,不过其中方法的返回值都是ALLOWED,也就是对于要校验的类及其子类的有效性都为允许,但是这样会有安全隐患,并不推荐使用。

1.2 方法

接下来看看PolymorphicTypeValidator其中的方法。

(1)public abstract Validity validateBaseType(MapperConfig<?> config, JavaType baseType);

用于检查基类所代表的所有子类的有效性,子类必须是该类的实例并且能够被Jackson core验证其兼容性。config通常是DeserializationConfig。

(2)public abstract Validity validateSubClassName(MapperConfig<?>config, JavaType baseType, String subClassName)

用于检查相关类子类的有效性,返回值不能为null。如果返回值不为DENIED,类的名字会被解析成java.lang.class

(3)public abstract Validity validateSubType(MapperConfig<?> config, JavaType baseType, JavaType subType)

在类的名字能被解析为实际类型后该方法被调用,比如先前调用了validateSubClassName方法并且返回了Validity.INDETERMINATE。validator应该能够决定其有效性并且返回正确的Validity的值。subType是要验证的已经被解析出来的子类型。

1.3使用

接下来看一下PolymorphicTypeValidator的使用。1.首先是BaseSettings中。该类是一个final类,用于存储一些基础的全局设置。其中有一个PolymorphicTypeValidator类型的属性_typeValidator,是用于处理不受信任的内容时,也就是需要验证类是否需要能够被反序列化。

2.其次在MapperConfig中有getPolymorphicTypeValidator的方法,该方法是返回该类中BaseSettings属性中的PolymorphicTypeValidator对象。

3.接着是在ObjectMapper中的使用。首先该类中也有枚举属性,结合该类中的activateDefaultTyping方法来列出哪些类型的类应该在默认的情况下使用。也就是没有明确声明的类型信息被找到时使用,而这个枚举类型则进一步限制出了使用的类的范围。当要处理的内容来自于不受信任的源的时候就使用PolymorphicTypeValidator来进行有效性校验。该类中的公共静态内部类DefaultTypeResolverBuilder中还有一个protected访问限制的PolymorphicTypeValidator类型的对象_subtypeValidator。ObjectMapper中有一名为_deserializatioConfig的属性,其中有一BaseSettings属性,而BaseSettings中是有一个PolymorphicTypeValidator对象的,所以很明显PolymorphicTypeValidator是在ObjectMapper进行反序列化也就是从json中还原对象时,当类型来自不被信任的源的时候会通过反序列化配置中基本配置中的PolymorphicTypeValidator进行有效性校验,以预防安全问题。

4.在StdTypeResolverBuilder中的使用,其中有一方法subTypeValidator用于获取一个PolymorphicTypeValidator对象,其为MapperConfig中BaseSettings中的PolymorphicTypeValidator对象。

protected PolymorphicTypeValidator verifyBaseTypeValidity(MapperConfig<?> config, JavaType baseType)

帮助处理可能的基类和子类的混合约束,如果baseType的有效性为DENIED,则抛出参数非法异常提示配置的PolymorphicTypeValidator拒绝了所有子类的处理。如果为ALLOWED,则返回一个LaiseezFaireSubTypeValidator的对象,该对象中的校验方法返回的都是ALLOWED。

5.在DatabindContext中的使用。这个类是被DeserializationContext和SerializerProvider所共享的,context对象通过data-binding进程进行传递。这样一来一些实现就能够依赖上一些共享的切面,比如二级环境对象类型工厂和处理器实例化器。

public JavaType resolveAndValidateSubType(JavaType baseType, String subClass, PolymorphicTypeValidator ptv)

和resolveSubType类似,但是这个方法不仅会查找出子类型还会根据所给的PolymorphicTypeValidator来校验结果子类型的有效性。

public JavaType _resolveAndValidateGeneric(JavaType baseType, String subClass, PolymorphicTypeValidator ptv, int index)

该方法与上面的方法类似,上面的方法基类验证结果为INDETERMINATE时才去校验子类是否为ALLOWED,而本方法只要结果不为ALLOWED且没被拒绝时就去校验子类是否为ALLOWED。

6.在MappeBuilder中的使用。和在ObjectMapper中的使用类似,有setter和activateDefaultTyping方法。activate方法确定了哪些范围的类在没有明确声明的信息的情况下使用validator校验。此外还有activateDefaultTypingAsProperty方法,该方法额外使用了inclusion机制。自动包含了类型信息以及明确的属性名。

此外在ClassNameIdResolver中也有一个protected访问限制的属性:PolymorphicTypeValidator,名为_subTypeValidator,由此可见是用于校验子类的。并且这个类是TypeIdResolver的具体实现类,在全限定java类名和json之间做转换。同样的在ClassNameResolver 的子类MinimalClassNameIdResolver中也有PolymorphicTypeValidator属性。

2.白名单的基本实现

此外官方提供了一个比较规范的实现类BasicPolymorphicTypeValidator,继承了PolymorphicTypeValidator的内部类Base。绝大多数情况下这个类被用于通过超类和java包名来决定那些已知的安全的子类通过的。该类中主要的属性有Set<Class<?>> _invalidBaseTypes,TypeMatcher[] _baseTypeMatchers,NameMatcher[] _subTypeNameMatchers,TypeMatch[] _subClassMatchers。这些属性存储着已知的匹配器,用于不同方式的校验,带有sub的适用于校验子类,例如_subClassMatchers校验子类是否是matcher里的类或者是其子类。NameMatcher有两种校验规则一是通过pattern,二是通过特定的前缀。只要对应的类符合其中一个匹配器的匹配规则就返回ALLOWED,并且不再需要其他的校验,否则就返回INDETERMINATE。此外返回DENIED的匹配器只有一种,并且只能通过class来判断,只适用于基于基类Class的判断。校验基类Class时,首先在_invalidBaseTypes中匹配,若有符合的则返回DENIED,接着在_baseTypeMatchers中匹配,若有符合的则返回ALLOWED,否则返回INDETERMINATE。

通过allowIf开头的方法来添加匹配器,allow开头的方法中又调用了append开头的方法通过重写对应的matcher 的方法向集合中添加想要的matcher。

该类不直接通过构造方法得到实例,而是通过build方法,其中调用protected的构造方法,间接的得到实例。并且上述的这些主要的东西都是定义在一个public的静态内部类中,BasicPolymorphicTypeValidator类中主要是重写了PolymorphicTypeValidator.Base的方法,定义了匹配器Matcher的接口还有构造builder实例的方法。