Fastjson 是 Alibaba 开发的 Java 语言编写的高性能 JSON 库,用于将数据在 JSON 和 Java Object 之间互相转换。

两个接口:

  1. JSON.toJSONString 将 Java 对象转换为 json 对象,序列化的过程。
  2. JSON.parseObject/JSON.parse 将 json 对象重新变回 Java 对象;反序列化的过程

poe.xml

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.24</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>

先定义一个Student类,方便后面使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import java.util.Properties;

public class Student {
public String name;
private int age;
private String address;
private Properties properties;

public Student() {
System.out.println("构造函数");
}

public String getName() {
System.out.println("getName");
return name;
}

public void setName(String name) {
System.out.println("setName");
this.name = name;
}

public int getAge() {
System.out.println("getAge");
return age;
}

public String getAddress() {
System.out.println("getAddress");
return address;
}

public Properties getProperties() {
System.out.println("getProperties");
return properties;
}
}

几个参数:

SerializerFeature.WriteClassName

JSON.toJSONString()中的一个设置属性值,设置之后在序列化的时候会多写入一个@type,即写上被序列化的类名,type可以指定反序列化的类,并且调用其getter/setter/is方法

Feature.SupportNonPublicField

如果需要还原出private属性的话,还需要在JSON.parseObject/JSON.parse中加上Feature.SupportNonPublicField参数

test:
需要先在Student类里加个setAge方法来测试,测完注释掉

1
2
3
4
5
6
7
8
9
10
11
12
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;

public class test {
public static void main(String[] args) {
Student student=new Student();
student.setName("jmx0hxq");
student.setAge(19);
System.out.println(JSON.toJSONString(student, SerializerFeature.WriteClassName));
}
}

输出

1
2
3
4
5
6
7
8
9
构造函数
setName
setAge
getAddress
getAge
getName
getProperties
{"@type":"Student","age":19,"name":"jmx0hxq"}

还原private属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;

public class test1 {
public static void main(String[] args) {
String jsonString ="{\"@type\":\"Student\",\"age\":19,\"name\":\"jmx0hxq\",\"address\":\"china\"}";
Object obj = JSON.parseObject(jsonString, Student.class,Feature.SupportNonPublicField);
// 或以下语句,输出结果一致
//JSONObject obj = JSON.parseObject(jsonString);
System.out.println(obj);
System.out.println(obj.getClass().getName());
System.out.println(((Student) obj).getAddress());
System.out.println(((Student) obj).getAge());
System.out.println(((Student) obj).getName());
}
}

输出

1
2
3
4
5
6
7
8
9
10
构造函数
setName
Student@4b9af9a9
Student
getAddress
china
getAge
19
getName
jmx0hxq

如果不加Feature.SupportNonPublicField属性,则无法得到私有属性结果就是:

1
Object obj = JSON.parseObject(jsonString, Student.class);

1
2
3
4
5
6
7
8
9
10
构造函数
setName
Student@4b9af9a9
Student
getAddress
null
getAge
0
getName
jmx0hxq

如果不加Student.class,则无法成功反序列化为Student类,而是JSONObject类:

1
Object obj = JSON.parseObject(jsonString);

1
2
3
4
5
6
7
8
getAddress
getAge
getName
getProperties
{"name":"jmx0hxq","age":0}
com.alibaba.fastjson.JSONObject
Exception in thread "main" java.lang.ClassCastException: com.alibaba.fastjson.JSONObject cannot be cast to Student
at test1.main(test1.java:12)

结论

  • 当反序列化为JSON.parseObject(*)形式,即未指定class时,会调用反序列化得到的类的构造函数、所有属性的getter方法、JSON里面的非私有属性的setter方法(==这里是因为private属性的变量不应该有set方法,如果自己加个set方法可以发现是会调用的,如果调用了自己设置的set方法,那反序列化的时候即使不加Feature.SupportNonPublicField也能得到它的值==),其中properties属性的getter方法会被调用两次

  • 当反序列化为JSON.parseObject(*,*.class)形式即指定class时,只调用反序列化得到的类的构造函数、JSON里面的非私有属性的setter方法(==这里是因为private属性的变量不应该有set方法,如果自己加个set方法可以发现是会调用的,如果调用了自己设置的set方法,那反序列化的时候即使不加Feature.SupportNonPublicField也能得到它的值==)、properties属性的getter方法;

  • 当反序列化为JSON.parseObject(*)形式即未指定class进行反序列化时得到的都是JSONObject类对象,而只要指定了class即JSON.parseObject(*,*.class)形式得到的都是特定的Student类;

parse和parseObject的区别

主要的区别就是parseObject()返回的是JSONObject,而parse()返回的是实际类型的对象
上面的代码我们直接Object obj = JSON.parse(jsonString);就可以得到Object obj = JSON.parseObject(jsonString, Student.class);一样的结果

这里参考drun1baby师傅的博客解释:

parseObject() 会额外的将Java对象转为 JSONObject对象,即 JSON.toJSON()。所以进行反序列化时的细节区别在于,parse() 会识别并调用目标类的 setter 方法及某些特定条件的 getter 方法,而 parseObject() 由于多执行了 JSON.toJSON(obj),所以在处理过程中会调用反序列化目标类的所有 settergetter 方法。

漏洞原理

fastjson 在反序列化的时候会去找我们在 @type 中规定的类是哪个类,然后在反序列化的时候会自动调用这些 setter 与 getter 方法和构造函数的调用

1
2
3
4
5
6
7
8
9
满足条件的setter:
- 非静态函数
- 返回类型为void或当前类
- 参数个数为1个

满足条件的getter:
- 非静态方法
- 无参数
- 返回值类型继承自Collection或Map或AtomicBoolean或AtomicInteger或AtomicLong

如果这些函数有危险函数则可以造成漏洞

参考

  1. https://drun1baby.top/2022/08/04/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Fastjson%E7%AF%8701-Fastjson%E5%9F%BA%E7%A1%80/
  2. Y4tacker/JavaSec: a rep for documenting my study, may be from 0 to 0.1 (github.com)