fastjson 2.0.63 是一次应优先处理的安全更新。该版本强化了 AutoType 反序列化校验,同时修复了多个可由构造输入触发的解析健壮性问题,包括内存耗尽(OOM)和拒绝服务(DoS)风险。
如果应用会接收外部 JSON 或 JSONB 数据,例如开放 API、消息队列事件、文件导入和跨系统 RPC,就不应把这次升级当成普通依赖维护。攻击者不一定需要绕过业务鉴权,只要能够让服务解析特制输入,就可能消耗大量内存或计算资源。
这次更新影响哪些应用
需要重点排查的不是“项目是否直接调用 fastjson2”,而是“不可信数据是否最终进入 fastjson2 解析器”。常见入口包括:
- HTTP 请求体、Webhook 和开放平台回调
- Kafka、RocketMQ 等消息系统中的事件载荷
- 用户上传的 JSON 或 JSONB 文件
- 缓存、数据库字段中保存的序列化对象
- 网关、RPC 框架或第三方 SDK 间接引入的 fastjson2
- 开启或依赖 AutoType 的多态反序列化流程
AutoType 允许解析器根据输入中的类型信息恢复具体 Java 类型。它能简化多态对象处理,但也扩大了输入对运行时类型选择的影响。因此,只要数据来源不完全可信,就需要对类型白名单、可实例化类和反序列化入口保持严格控制。
OOM 与 DoS 风险则不一定依赖 AutoType。恶意输入可能利用异常长度、深度、容器结构或特定编码组合迫使解析器分配大量内存,或长时间占用 CPU。升级和资源边界控制需要同时进行,不能只依赖某一个开关。
直接升级到 2.0.63
Maven 项目可以将 fastjson2 版本显式更新为 2.0.63:
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.63</version>
</dependency>
Gradle 项目对应配置如下:
dependencies {
implementation("com.alibaba.fastjson2:fastjson2:2.0.63")
}
修改后,应检查最终解析出来的依赖版本,避免父 POM、BOM 或传递依赖仍然锁定旧版本:
# Maven
mvn dependency:tree -Dincludes=com.alibaba.fastjson2
# Gradle
./gradlew dependencyInsight \
--dependency fastjson2 \
--configuration runtimeClasspath
对于多模块仓库,建议在 dependency management 或版本目录中集中固定版本,而不是逐个模块修改。这样可以减少同一进程中出现多个 fastjson2 版本的概率。
暂时不能升级时启用安全模式
如果受发布窗口、兼容性验证或上游框架限制而无法立即升级,可以通过 JVM 系统属性启用解析器安全模式,以缓解 AutoType 相关问题:
java \
-Dfastjson2.parser.safeMode=true \
-jar app.jar
在 Kubernetes 中,可以通过 JAVA_TOOL_OPTIONS 注入该参数:
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
template:
spec:
containers:
- name: order-service
image: example/order-service:1.8.0
env:
- name: JAVA_TOOL_OPTIONS
value: "-Dfastjson2.parser.safeMode=true"
部署后可检查进程参数,确认配置确实进入运行中的 JVM:
kubectl exec deploy/order-service -- \
sh -c 'jcmd 1 VM.system_properties | grep fastjson2.parser.safeMode'
需要明确的是,安全模式是无法立即升级时的临时缓解措施,重点针对 AutoType 风险。它不能替代 2.0.63 中的完整解析健壮性修复,也不应被视为 OOM 或 DoS 问题的统一解决方案。启用后还要回归测试依赖多态反序列化的功能,因为更严格的类型处理可能改变原有行为。
给解析入口增加资源边界
依赖升级之外,应用还应限制攻击者能够提交的数据规模。下面是一个可以改造的 Spring Boot 示例,在进入 fastjson2 前检查请求体大小,并避免直接反序列化为由客户端决定的任意类型:
package example.api;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.nio.charset.StandardCharsets;
@RestController
public class ImportController {
private static final int MAX_JSON_BYTES = 1024 * 1024;
public record ImportRequest(String orderId, int quantity) {}
@PostMapping("/imports")
public ResponseEntity<?> importData(@RequestBody byte[] body) {
if (body.length > MAX_JSON_BYTES) {
return ResponseEntity.status(HttpStatus.PAYLOAD_TOO_LARGE)
.body("JSON payload exceeds 1 MiB");
}
try {
ImportRequest request = JSON.parseObject(
new String(body, StandardCharsets.UTF_8),
ImportRequest.class
);
return ResponseEntity.ok(request);
} catch (JSONException ex) {
return ResponseEntity.badRequest().body("Invalid JSON payload");
}
}
}
这个示例中的 1 MiB 只是实践假设,实际值应根据业务数据分布设定。网关、Servlet 容器和应用层最好同时配置限制,避免超大请求在抵达业务代码前就占用过多连接、缓冲区或堆内存。
对于 JSONB、消息队列和文件导入,也应采用相同思路:限制单条消息或文件大小,设置消费超时和并发上限,并将解析失败视为不可重试或有限重试事件,避免同一恶意载荷形成重试风暴。
升级验证清单
升级不应只验证“服务能启动”。更有效的检查包括:
- 确认生产运行时实际加载的是 fastjson2 2.0.63
- 回归 JSON 与 JSONB 的正常序列化、反序列化流程
- 测试多态对象、历史数据和启用 AutoType 的兼容场景
- 对超大、截断、深度嵌套和类型异常输入执行负向测试
- 观察解析失败率、CPU、堆内存、GC 暂停和容器重启次数
- 检查网关、消息系统和文件上传入口是否设置大小限制
- 清理不再需要的 AutoType 配置和宽泛类型许可
fastjson 2.0.63 的核心价值不只是修复某一个反序列化条件,而是同时收紧类型校验并提升解析器面对恶意输入时的稳定性。最稳妥的处理顺序是尽快升级、验证最终依赖版本,再为所有不可信数据入口补上大小、类型、超时和并发边界。安全模式可以争取修复时间,但不应成为长期停留在旧版本上的理由。