01漏洞编号
CVE-2022-25845
CNVD-2022-40233
CNNVD-202206-1037
二、Fastjson知多少
万恶之源AutoType
Fastjson的主要功能是将Java Bean序列化为JSON字符串,这样得到的字符串就可以通过数据库等方式进行持久化了。
但是,Fastjson在序列化及反序列化的过程中,没有使用Java自带的序列化机制,而是自定义了一套机制。
对于JSON框架来说,想要把一个Java对象转换成字符串,有两种选择:
1、基于属性
2、基于Setter/Getter
在我们常用的JSON序列化框架中,Fastjson和Jackson将对象序列化成Json字符串时,是通过遍历该类中所有的Getter方法来进行的。而Gson不是这么做的,它是通过反射遍历该类中的所有属性,并把其值序列化为Json 。
假设我们有下面这个Java类:
class Store {
private String name;
private Fruit fruit;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Fruit getFruit() {
return fruit;
}
public void setFruit(Fruit fruit) {
this.fruit = fruit;
}
}
interface Fruit {
}
class Apple implements Fruit {
private BigDecimal price;
//省略 setter/getter、toString等
}
//所有网络安全全套资料免费领取加w:anquan455领取~
当我们对它进行序列化时,Fastjson会扫描其中的Getter方法,即找到getName和getFruit,这时就会将Name和Fruit两个字段的值序列化到JSON字符串中。
那么问题来了,上面定义的Fruit只是一个接口,序列化的时候Fastjson能将属性值正确序列化出来吗?如果可以的话,反序列的时候,Fastjson会把这个Fruit反序列化成什么类型呢?
我们尝试基于Fastjson v1.2.68验证一下:
Store store = new Store();
store.setName("Hollis");
Apple apple = new Apple();
apple.setPrice(new BigDecimal(0.5));
store.setFruit(apple);
String jsonString = JSON.toJSONString(store);
System.out.println("toJSONString : " + jsonString);
以上代码比较简单,我们创建了一个store,为它指定了名称,并创建了Fruit的子类型Apple,然后将store用JSON.toJSONString进行序列化,可以得到以下JSON内容:
toJSONString : {
"fruit":{
"price":0.5
}
,"name":"Hollis"
}
那么,Fruit的类型是什么呢,能否反序列化为Apple呢?我们再来执行以下代码:
Store newStore = JSON.parseObject(jsonString, Store.class);
System.out.println("parseObject : " + newStore);
Apple newApple = (Apple)newStore.getFruit();
System.out.println("getFruit : " + newApple);
执行结果如下:
toJSONString : {
"fruit":{
"price":0.5
}
,"name":"Hollis"
}
parseObject : Store{
name='Hollis', fruit={}}
Exception in thread "main" java.lang.ClassCastException: com.hollis.lab.fastjson.test.$Proxy0 cannot be cast to com.hollis.lab.fastjson.test.Apple
at com.hollis.lab.fastjson.test.FastJsonTest.main(FastJsonTest.java:26)
可以看到,在将store反序列化后,我们尝试将Fruit转换成Apple,但抛出了异常,如果直接转换成Fruit则不会报错,如下:
Fruit newFruit = newStore.getFruit();
System.out.println("getFruit : " + newFruit);
从以上现象中我们得知,当一个类中包含了一个接口(或抽象类)的时候,使用Fastjson进行序列化,会将子类型抹去,只保留接口(抽象类)的类型,使得反序列化时无法拿到原始类型。
如何解决这个问题呢?Fastjson引入了AutoType
,在序列化时,把原始类型记录下来。使用方