本篇文章主要对FastJSON AutoType的校验原理,以及绕过方式进行简单的分析介绍,跟多的是学习记录,文章涉及的绕过方式都是"站在巨人的肩膀上"看风景的,很后悔当初去看了Jackson-databind而丢弃了fastJSON,哎....,悔不当初呀~
FastJSON中的checkAutoType()函数用于对反序列化的类进行黑白名单校验,我们首先来看一下checkAutoType()函数的检查流程:
代码位置:fastjson-1.2.68\src\main\java\com\alibaba\fastjson\parser\ParserConfig.java
checkAutoType函数默认需要传递三个参数:
String typeName:被序列化的类名
Class<?> expectClass:期望类()
int features:配置的feature值
这里的expectClass(期望类)的目的是为了让一些实现了expectClass这个接口的类可以被反序列化,可以看到这里首先校验了typeName是否为空、autoTypeCheckHandlers是否为null,之后检查safeMode模式是否开启(在1.2.68中首次出现,配置safeMode后,无论白名单和黑名单都不支持autoType)、typeName的长度来决定是否开启AutoType:
public Class<?> checkAutoType(String typeName, Class<?> expectClass, int features) { if (typeName == null) { return null; } if (autoTypeCheckHandlers != null) { for (AutoTypeCheckHandler h : autoTypeCheckHandlers) { Class<?> type = h.handler(typeName, expectClass, features); if (type != null) { return type; } } } final int safeModeMask = Feature.SafeMode.mask; boolean safeMode = this.safeMode || (features & safeModeMask) != 0 || (JSON.DEFAULT_PARSER_FEATURE & safeModeMask) != 0; if (safeMode) { throw new JSONException("safeMode not support autoType : " + typeName); } if (typeName.length() >= 192 || typeName.length() < 3) { throw new JSONException("autoType is not support. " + typeName); }
然后判断期望类expectClass,从下面可以看到这里的Object、Serializable、Cloneable、Closeable、EventListener、Iterable、Collection都不能作为期望类:
final boolean expectClassFlag; if (expectClass == null) { expectClassFlag = false; } else { if (expectClass == Object.class || expectClass == Serializable.class || expectClass == Cloneable.class || expectClass == Closeable.class || expectClass == EventListener.class || expectClass == Iterable.class || expectClass == Collection.class ) { expectClassFlag = false; } else { expectClassFlag = true; } }
之后从typeName中解析出className,然后计算hash进行内部白名单、黑名单匹配,之后如果不在白名单内且未开启AutoType或者expectClassFlag为true则进行hash校验——白名单acceptHashCodes、黑名单denyHashCodes,如果在白名单内就加载,在黑名单中就抛出异常:
String className = typeName.replace('$', '.'); Class<?> clazz; final long BASIC = 0xcbf29ce484222325L; final long PRIME = 0x100000001b3L; final long h1 = (BASIC ^ className.charAt(0)) * PRIME; if (h1 == 0xaf64164c86024f1aL) { // [ throw new JSONException("autoType is not support. " + typeName); } if ((h1 ^ className.charAt(className.length() - 1)) * PRIME == 0x9198507b5af98f0L) { throw new