如果把 Java 的安全史整理成一份 CVE 清单,它很快就会膨胀成一本翻不到头的流水账。漏洞数量庞大,编号一年比一年长,但真正读下来,却很难看清两件更要紧的事:研究的重心是怎样一步步转移的,以及攻击者为什么总能在 Java 生态里找到新的入口。
Black Hat 历年的议题,正好适合用来梳理这条脉络。它算不上完整的漏洞数据库,也无法代表某一年的全部研究成果,却能清晰地反映出几个层面的变化:研究者当时最关注什么,哪些攻击手法已经成熟落地,又有哪些问题正从小范围的学术探讨走向大规模的真实攻击。
本文覆盖 2002—2026 年。标题里的“20年”是二十余年的概括说法,实际观察区间接近 24 年。
一、2002—2010:最先被盯上的,是 JVM 这道关口
2002 年的 Black Hat Asia 有一个很直接的题目:《Java and Java Virtual Machine Security Vulnerabilities and Their Exploitation Techniques》。议题内容包括 Java 内置安全机制、Applet sandbox、SecurityManager、bytecode verifier,以及当时不同 JVM 实现中的安全漏洞和 sandbox escape 技术。[1]
同年的 Black Hat USA 还有《Security Aspects in Java Bytecode Engineering: A Tutorial》,内容从 JVM 架构和 bytecode 讲起,也讨论了如何借助字节码工具分析和修改程序。[2]
这与当时 Java 的典型用法高度吻合。浏览器会从网络上下载 Applet,再交给本地 JVM 执行。远程代码固然能跑起来,但理论上只能被关在沙箱里,无法随意读取文件、发起网络连接,或调用本地资源。
当时最重要的几道防线是:
- bytecode verifier 检查 class 文件和指令是否合法;
- ClassLoader 区分本地可信代码和远程代码;
- SecurityManager 根据策略限制敏感操作;
- Applet sandbox 把远程代码关在一个受限环境中。
因此早期的 Java 安全研究,大多围绕类型安全、类加载和权限检查展开。只要能让 JVM 对某段 bytecode 的类型判断出现偏差,或是诱使高权限类替不可信代码执行操作,沙箱就有可能被绕过。
JavaSnoop 把问题拉回到客户端信任
到了 2010 年,Black Hat USA 的《JavaSnoop: How to Hack Anything Written in Java》给出了另一种思路:直接挂载到正在运行的 JVM 上,修改 class bytecode、方法参数与返回值,甚至能在对象进入网络之前将其拦截、篡改。[3]
同一届会议的《Hacking Java Clients》同样把重点放在 Java 客户端本身,探讨了客户端的业务逻辑、授权判断,以及运行时对象一旦被篡改会带来哪些后果。[3:1]
这批研究把一个朴素、却常被忽视的事实摆到了台面上:
只要代码运行在用户控制的机器上,客户端里的权限判断就不能算真正的安全边界。
许可证校验、角色判断、金额计算、功能开关,都可以放在客户端以改善体验,但服务端必须再做一次校验。混淆、签名和私有字段顶多抬高分析门槛,并不能改变"客户端始终掌握在用户手里"这个前提。
二、2011—2013:Java 浏览器插件进入利用高峰
很多人习惯把 Java 称作"内存安全语言",但这一说法其实只在语言层面部分成立。JVM、JIT、JNI 以及字体和图形处理等组件里,仍然存在大量 native code。
Black Hat Abu Dhabi 2011 的《Exploiting Memory Corruption Vulnerabilities in the Java Runtime》专门讨论了 JRE 中 memory corruption 漏洞的利用,以及开发可靠 exploit 时需要处理的问题。[4]
2012 年的《Recent Java Exploitation Trends and Malware》则把视角放到真实攻击。官方摘要提到,当时越来越多 Java 漏洞被用于恶意软件传播;Java 的跨平台特性让同一个 sandbox breach 有机会覆盖 Windows、macOS 和 Unix 系统。议题还以 CVE-2012-0507 为例,说明这类漏洞已经被用于大规模感染。[5]
这一阶段 Java 对攻击者很有吸引力,原因不复杂:
- 桌面安装量大;
- 浏览器插件直接接收网络内容;
- 很多终端长期没有升级 JRE;
- sandbox escape 往往比完整的 native exploit chain 更容易稳定;
- 一套 Java exploit 可以覆盖多个操作系统。
2013 年的《Java Every-Days: Exploiting Software Running on 3 Billion Devices》又往前走了一步。议题明确提到,研究重点已经不再局限于传统 memory corruption,而是开始利用 reflection API 等 Java 动态能力实现 remote code execution。[6]
换句话说,攻击者不一定非要破坏堆内存。只要能绕过访问限制,拿到原本不该访问的类、方法或对象,同样可以调用高权限 API。
这段历史,到 2026 年基本画上了句号
早期 Java 沙箱依赖 SecurityManager 和 Applet。后来浏览器插件退出主流,平台自身也开始清理这些旧机制。
JEP 411 在 JDK 17 中把 SecurityManager 标记为待移除;JEP 486 在 JDK 24 中将其永久禁用;JEP 504 则在 JDK 26 中移除了 Applet API。[7][8][9]
这并不意味着 Java 不再需要隔离,而是隔离的位置发生了转移。如今更可靠的边界,往往落在 JVM 之外——独立进程、容器、操作系统用户、文件权限、网络策略以及云身份。
三、2014—2019:主战场转向服务器端
浏览器插件退场之后,Java 安全并没有随之降温。企业内部的 Java Web 应用、RPC、消息系统、应用服务器和中间件,成了新的主要攻击面。
运行时数据流开始被自动追踪
Black Hat USA 2014 的 RAVAGE,全名是《Runtime Analysis of Vulnerabilities and Generation of Exploits》。它通过正常程序执行追踪完整 dataflow,用于发现漏洞、生成 exploit,并把结果接入现有测试框架。研究者还公开了 Java 版本。[10]
这类工作很适合 Java。class 文件格式统一,类型和方法信息相对完整,JVM 又提供 instrumentation 能力。研究工具可以在不拿到完整源码的情况下观察方法调用、对象传播和敏感操作。
从这里开始,Java 漏洞分析不再只是搜一个危险函数,而是要回答三个问题:
- 外部输入从哪里进来;
- 中间经过了哪些对象、回调和类型转换;
- 最后是否真的能到达文件、网络、进程执行或动态类加载。
2016 年,Java Serialization 成为企业级攻击入口
Black Hat USA 2016 的《Pwning Your Java Messaging with Deserialization Vulnerabilities》把 Java deserialization 放进了消息系统。议题指出,Java 生态中的消息处理大量依赖 Java Serialization,因此 message consumer 也可能暴露不安全反序列化入口。[11]
Java 原生反序列化的问题,远不止"解析了一个不可信文件"这么简单。ObjectInputStream.readObject() 恢复的是一整张对象图,恢复过程中还会顺带触发 readObject、readResolve、compare、getter 等方法。攻击者根本不必往服务器上传新代码,只要目标 classpath 里凑齐了合适的类,就能把这些看似正常的方法串成一条 gadget chain。
一条典型的链路通常由四部分组成:
- 网络、文件、消息或 RPC 中,出现攻击者可控的序列化数据;
- 服务端调用 Java Object Serialization 还原对象;
- 对象图依次触发一组已有类的方法;
- 最终落入文件写入、网络访问、脚本执行、模板执行或进程创建等 sink。
入口往往只有一个,但真正决定能否利用的,却是整个 classpath。应用的依赖越多,候选 gadget 就越多,判断的难度也随之上升。
同一年,JNDI 被正式确立为一种攻击原语
Black Hat USA 2016 的另一个关键议题是《A Journey from JNDI/LDAP Manipulation to Remote Code Execution Dream Land》。研究者提出了 "JNDI Reference Injection",并指出:一旦攻击者可控的名称被传入 JNDI lookup,就可能借助 RMI、LDAP、CORBA 等服务,反过来影响执行查询的服务器。[11:1]
JNDI 原本是企业 Java 的基础设施 API,用于查找 DataSource、EJB、消息队列和目录对象。问题恰恰出在:应用把查询名称、Provider URL 乃至目录内容,交给了不可信输入去决定。
真正的危险点,通常不是 InitialContext.lookup() 这一个方法,而是下面几个条件同时成立:
- 外部输入能够控制 lookup 的名称或目标;
- JNDI provider 支持远程对象或引用;
- 服务端能够主动访问攻击者可控的网络位置;
- 查询结果会触发对象创建、类加载或其他动态行为。
这条研究线索,后来会在 Log4Shell 中再度浮现。
换成 JSON,不代表对象注入问题消失
Java deserialization 大规模曝光后,不少团队的第一反应是改用 JSON。Black Hat USA 2017 的《Friday the 13th: JSON Attacks》正是针对这种想当然的结论。研究者分析了 Java 和 .NET 的主流 JSON parser,证明部分库在默认或常见配置下依然能被打成 RCE,并把这套思路推广到了其他 serialization format。[12]
问题的关键,从来不在于数据长得像二进制、XML 还是 JSON,而在于 parser 会不会根据输入去决定具体的类型。
一旦外部数据能够控制 polymorphic type、类名、构造过程或属性 setter,所谓的"数据解析"就可能悄然变成对象实例化。只要后面还能接上危险类和 gadget,换个数据格式并没有真正切断攻击路径。
2018 年:gadget chain 走向自动化,组件间的解释差异也进入视野
Black Hat USA 2018 的《Automated Discovery of Deserialization Gadget Chains》提出了自动发现 Java gadget chain 的方法。在此之前,很多工具能定位不安全的 deserialization entry point,但要判断它究竟能不能利用,往往还得靠人工翻遍 classpath。这个议题真正把重心放到了"自动找出可用调用链"上。[13]
同年的《Breaking Parser Logic: Take Your Path Normalization off and Pop 0days Out!》则聚焦 path normalization 这一类攻击面,覆盖了包括 Java 在内的多种 Web framework。[13:1]
把这两项研究并置来看很有意思:一个关注对象图内部的调用关系,另一个关注代理、容器与 framework 对同一条路径的不同理解。它们指向的都不是某个单点的危险函数,而是系统各组件之间没有遵循同一套语义。
到了 Black Hat Europe 2019,《New Exploit Technique In Java Deserialization Attack》依旧在列。至少从议题的延续性可以看出,Java deserialization 绝非在 2016 年火过一阵便偃旗息鼓。[14]
四、2021:Log4Shell 把积累多年的问题一次性引爆
CVE-2021-44228,即人们熟知的 Log4Shell。NVD 的漏洞描述指出,受影响的 Log4j 2 版本中,JNDI 功能没有对攻击者可控的 LDAP 等 JNDI endpoint 做出正确防护,而攻击入口可以来自 configuration、log message 甚至普通 parameter。[15]
CISA 随后指出,Log4j 被集成进了全球数千种产品。[16] 这也解释了为什么当时许多公司最棘手的工作并非编写 WAF 规则,而是先要回答一个更基础的问题:生产环境里究竟有哪些系统带着这个版本的 Log4j。
Log4Shell 并非凭空冒出的全新手法,它真正做的,是把几条早已存在多年的研究线索汇到了一处:
- 普通业务输入进入日志;
- 日志组件把字符串当成可解释的内容;
- Lookup 机制触发 JNDI;
- 服务端允许访问外部 LDAP 等目标;
- 组件运行在高权限的 Java 进程里;
- Log4j 又被大量产品间接打包、层层二次封装。
2016 年的 JNDI 研究点明了这项能力的危险所在,而 2021 年的 Log4Shell 则用现实证明:一个底层通用库一旦把普通输入接到这项能力上,波及面会大到什么程度。
它也暴露了 Java 生态长期存在的三个管理难题:
- 依赖可能藏在 Fat JAR、WAR、EAR、插件目录或厂商产品里;
- 构建文件里的依赖版本不一定等于运行时真正加载的版本;
- 即使应用代码没有直接使用某个功能,间接依赖仍可能把它带进来。
五、2023—2025:工具不再只找入口,还要证明整条链跑得通
ODDFuzz 把静态分析和 directed greybox fuzzing 接了起来
Black Hat USA 2023 的《ODDFuzz: Hunting Java Deserialization Gadget Chains via Structure-Aware Directed Greybox Fuzzing》继续解决 gadget chain 自动发现问题。[17]
ODDFuzz 先做轻量级 static taint analysis,找出候选 gadget chain;再使用 structure-aware seed generation 和 directed greybox fuzzing 生成能够触发候选链的序列化对象。它的目标很明确:静态分析负责尽量别漏,动态验证负责压低误报。[18]
论文给出的实验结果是,在 ysoserial 数据集中,它发现了 34 条已知链中的 16 条,两种对比方案只能找到 3 条;在 WebLogic、Dubbo、Nexus 和 protostuff 等真实目标中,研究还发现了 6 条此前未报告的可利用链,并获得 5 个 CVE。[18:1]
这里最关键的进步,并不在于数字,而在于研究标准被抬高了。
仅仅找到 readObject() 已经不够,仅仅确认 source 到 sink 可达也不够。工具开始尝试构造真实的对象,去满足字段关系、类型约束和运行时分支,最终证明这条链确实能够被触发。
JDD 继续解决 path explosion 和 payload construction
Black Hat Asia 2025 的 JDD 议题全名是《In-depth Mining of Java Deserialization Gadget Chains via Bottom-up Gadget Search and Dataflow-aided Payload Construction》。[19]
JDD 项目先把调用关系总结成 gadget fragment,再使用 bottom-up search 连接候选片段,同时借助 dataflow 关系辅助 payload construction。项目论文发表于 2024 IEEE Symposium on Security and Privacy,工具支持 JDK、Hessian、JSON 等多种 deserialization protocol。[20]
从 2018、2023 再到 2025,这条工具的演进路线相当清晰:
- 2018 年,重点是自动找出调用链;
- 2023 年,重点是用结构感知 fuzzing 验证候选链;
- 2025 年,重点是压制 path explosion,并解决对象字段关系与 payload construction。
Java 反序列化研究,已经从"是否存在危险 API"迈入了"能否自动产出一条可验证攻击链"的阶段。
六、2026:新方向是语义差异、中间件的认证前缺陷与 JVM 内存马
2026 年的 Black Hat Java 议题不再只围绕反序列化打转,而是冒出了三个格外值得关注的方向。
Cast Attack:同一份输入,在字符层和字节层被读成两种含义
Black Hat Asia 2026 的《Cast Attack: A New Threat Posed by Ghost Bits in Java》研究了 Java 类型转换中的数据丢失,以及这种丢失如何催生出新的攻击面。[21]
Oracle 的 Java 26 API 文档对 DataOutputStream.writeBytes(String) 说得相当直白:字符串里的每个字符在写出时,高 8 位都会被直接丢弃。[22]
这里的风险,并不在于"类型转换会报错"——恰恰相反,它往往悄无声息,根本不报错。设想安全设备、校验逻辑或审计日志读到的是原始的 Unicode String,而后端组件最终取用的却是丢弃高位后的低 8 位字节,那么这两层对同一份输入的理解,就出现了分歧。
这类问题可以归入 parser differential 或 representation differential:安全检查依据的是一种表示,真正执行依据的却是另一种表示。此时只针对某个 payload 的特征打补丁往往治标不治本,开发者真正需要审查的,是从编码到类型转换的完整链条。
对 Java 代码来说,下面几类写法都值得专门审计:
char到byte的 narrowing conversion;- 用
writeBytes(String)写协议、路径或命令数据; - 未明确指定
Charset的字符串和字节转换; - 校验发生在编码之前,而危险操作发生在编码之后;
- WAF、Servlet container 和业务框架使用不同的 decode 规则。
企业级 Java:pre-auth RCE 依旧潜伏在中间件里
Black Hat USA 2026 的《Pre-auth RCE in Enterprise Java: When Middleware Becomes the Exploit》把视线重新拉回企业级 Java 平台。[23]
研究团队的公开文章称,他们审计了 4 个企业级 Java 平台,发现 12 个漏洞,其中包括 1 个 sandbox escape 和 4 个 pre-auth 问题,并展示了 Bonita BPM 与 Apache OFBiz 中两条完整、相互独立的 pre-auth RCE chain。[24]
这批问题没有依赖某个单一“万能 gadget”。共同模式是:
- 外部请求先到达公开入口;
- router、security filter、servlet dispatcher 或 SSO handler 对请求的判断不一致;
- 原本只对内部组件开放的接口,被未认证请求直接摸到;
- deserializer、template engine 或其他 execution sink 把可达性变成代码执行。
这类漏洞最容易被漏掉,因为单看每个组件,设计都挑不出毛病。router 以为 filter 会拦下来,filter 以为 dispatcher 不会这么解析,内部 API 又默认调用方早已通过认证。真正的漏洞,恰恰出现在这些彼此错位的假设被拼接到一起之后。
JVM 内存马:拿到 RCE 之后,运行时状态本身也成了研究对象
Black Hat Asia 2026 还有《More JVM Memory Shells: JVM Memory Shell Auto Searching Program》。官方日程将它归入 Malware 和 Threat Hunting;研究者介绍的是一个结合 static analysis、runtime instrumentation 和 in-memory inspection 的自动化框架,用来发现 JVM 内存马。[21:1][25]
JVM 内存马的棘手之处在于,它未必对应磁盘上的某个新文件。攻击者可能改写已经加载的 class,注册新的 Filter、Listener、Servlet,挂接框架的 handler,或是借助 Java Agent 与 instrumentation 悄悄改变运行时行为。
因此,只做文件扫描和依赖扫描远远不够。现代 Java 防守还必须掌握:
- JVM 里实际加载了哪些 class;
- 哪些 class 在运行中被重新定义;
- Web 容器里的 Filter、Listener、Servlet、handler 列表是否发生变化;
- 是否出现异常 ClassLoader、Java Agent、线程或动态字节码;
- 运行时状态是否与部署基线一致。
这一方向表明,Java 安全研究的视野,已经从"如何拿到代码执行"延伸到了"如何发现 JVM 内部的驻留与篡改"。
七、从这些议题里,能读出六个趋势
1. 安全边界从 JVM 内部扩展到了整个系统
2002 年的核心问题是 bytecode verifier 和 sandbox 能不能挡住不可信代码。2026 年的核心问题已经变成:一条请求经过 WAF、router、Servlet container、framework、serializer 和内部服务后,哪一层把它理解错了。
今天审计 Java 应用,至少要把下面这些东西放在一张图里:
- JDK 和 JVM 参数;
- framework 与 middleware;
- classpath 和插件;
- reverse proxy 与 API gateway;
- 消息队列和 RPC;
- 网络出口;
- 容器、云身份和运行时状态。
只看业务 Controller,通常看不完整。
2. memory safety 不是 Java 安全的终点
Java 语言减少了很多 use-after-free、buffer overflow 和手工内存管理问题,但严重漏洞仍然可以来自 reflection、deserialization、JNDI、expression language、template engine、ClassLoader、类型转换和路径解析。
这些问题所利用的,恰恰是合法的语义。攻击者无需把 JVM 打崩,只需诱导系统按一个危险却"合规"的顺序,去调用那些本就存在的功能。
3. 最危险的地方,往往在组件与组件的衔接处
path normalization、Cast Attack 以及 2026 年中间件的 pre-auth RCE,都指向同一个共性:不同组件对同一份输入,并没有达成一致的理解。
这类解读落差常见于:
- 原始 URL 与规范化后的 URL;
- Unicode 字符串与最终落地的字节;
- proxy path 与 Servlet path;
- public API 与 internal API;
- JSON 里的类型信息与 Java 实际加载的类;
- 构建期的依赖与运行时的 classpath。
单个组件各自通过了测试,并不代表它们组合起来依然安全。
4. 漏洞挖掘从“找到可疑点”走向“证明可利用”
RAVAGE、Gadget Inspector、ODDFuzz、JDD 这一类工作都在解决同一个现实问题:静态工具给出一条 source-to-sink 路径很容易,证明它在真实 classpath、真实对象状态和真实 framework 生命周期里能够触发,要难得多。
接下来的高价值工具会更关注:
- 对象和字段约束;
- reflection、proxy 和 callback;
- framework lifecycle;
- 配置与依赖版本;
- 动态验证;
- 最小可复现输入;
- 运行时副作用确认。
5. Java 供应链问题最终会落到运行时
Log4Shell 之后,SBOM 和 Software Composition Analysis 成了热门话题,它们固然重要,但 Java 很容易陷入"构建时看到的是一套,运行时加载的却是另一套"的尴尬。
Fat JAR、WAR、应用服务器共享目录、插件、Java Agent、容器镜像和厂商封装都会改变实际 classpath。真正有用的资产清单,必须能回答运行中的 JVM 加载了什么,而不是只回答 pom.xml 写了什么。
6. 老问题和新问题会长期共存
2026 年仍在研究企业级 Java 的 pre-auth RCE 和 JVM 内存马,恰恰说明老旧中间件、旧协议和 Java Serialization 远未退出生产环境;与此同时,Cast Attack 又把注意力引向了字符与字节的表示差异。
Java 生态的特点,从来不是某个时代彻底取代上一个时代,而是新旧攻击面层层叠加。一个现代的 Spring 应用,仍可能依赖十多年前设计的 serialization protocol,跑在更早的应用服务器上,外面再套一层新的 API gateway。
八、现在做 Java 安全,应该重点检查什么
1. 先减少反序列化入口,再谈 filter
跨信任边界尽量不要使用 Java Object Serialization。HTTP、消息队列、缓存、RPC 和文件导入都应使用结构明确的数据模型,并限制可实例化类型。
无法立即移除时,可以使用 JEP 290 的 ObjectInputFilter 限制 class、array size、object graph depth、reference count 和 stream size;JEP 415 又增加了 context-specific 和动态选择 filter 的能力。[26][27]
但 filter 是补偿措施,不是免死金牌。还要检查当前 classpath 中是否存在可利用 gadget,并确认 framework 有没有自己创建 ObjectInputStream 绕开全局配置。
2. JNDI 名称和 Provider URL 不能由外部输入决定
重点搜索并跟踪:
InitialContext.lookup(name);
Context.lookup(name);
JndiTemplate.lookup(name);
不要只看参数是不是“看起来像固定前缀”。需要继续检查字符串拼接、环境变量、Header、消息属性、数据库配置和目录内容能否影响最终 lookup。
网络侧应默认限制 Java 服务访问外部 LDAP、RMI、CORBA 和不必要的 DNS 目标。很多动态加载链需要服务端主动出网,egress control 能直接截断这一环。
3. 校验和使用必须基于同一份规范化结果
路径、URL、Header、Unicode 和协议字段应该在入口处完成一次明确转换。鉴权、路由、日志和业务逻辑尽量使用同一个 canonical representation。
涉及字符串转字节时,明确指定 Charset,避免把 writeBytes(String) 用在安全敏感数据上,也不要在校验后再做会丢信息的 narrowing conversion。
测试时要跑真实部署组合,包括 CDN、WAF、reverse proxy、Servlet container 和 framework。只在单元测试里调用 Controller,发现不了 parser differential。
4. internal API 也要有自己的认证和授权
不要把“外部路由访问不到”当作内部接口的唯一保护。内部 API、management endpoint、serializer、template engine 和 script executor 应在自身入口做认证、授权和输入约束。
如果组件确实只允许内部调用,最好使用独立 listener、网络 namespace、mTLS 或 workload identity,而不是只靠一个 path prefix。
5. 依赖清单要覆盖运行时 classpath
除了 SCA 和 SBOM,还应定期采集:
- JVM 实际加载的 JAR 和 module;
- Fat JAR、WAR、EAR 内嵌依赖;
- 应用服务器共享 library;
- plugin 和 Java Agent;
- 容器基础镜像里的重复版本;
- 同名 class 的实际加载来源;
- 动态下载和缓存的 artifact。
漏洞响应时,能快速回答“哪些 JVM 真的加载了受影响 class”,比只搜索 Git 仓库更有用。
6. 给 JVM 运行时做基线
生产环境可以记录 class loading、ClassLoader、Java Agent、Filter、Listener、Servlet、handler、线程和关键 system property 的基线。
发现下面这些变化时应触发调查:
- 未知 Java Agent attach;
- class retransformation 或 redefinition;
- 新增的 Filter、Listener、Servlet;
- 异常 ClassLoader;
- 只存在内存中、无法对应部署 artifact 的 class;
- 可疑线程和定时任务;
- framework registry 在运行中发生变化。
7. 把 RCE 后的权限压到最低
Java 进程不应默认拥有写应用目录、执行系统命令、访问云元数据、读取全量 Secret 或连接全部内网服务的权限。
更实际的防线包括:
- 独立低权限操作系统用户;
- read-only filesystem;
- 最小化容器镜像;
- 网络出口 allowlist;
- 最小权限 ServiceAccount 和云身份;
- 敏感管理面与业务面隔离;
- 数据库、消息队列和制品仓库使用短期凭据。
Java 层漏洞不可避免时,这些控制决定一次 RCE 最终能走多远。
结语
2002 年,研究者还在琢磨如何骗过 bytecode verifier 和 Applet sandbox。2016 年之后,注意力转向了 Java Serialization、JNDI 和企业中间件。而到了 2026 年,研究重点又延伸到字符与字节之间的语义差异、中间件认证前的解析分歧,以及 JVM 运行时中的内存马驻留。
表面上看,漏洞类型换了很多轮,核心问题其实一直很稳定:
谁在解释不可信输入,解释过程中调用了哪些动态能力,最后又拿到了什么权限。
Java 的强项在于成熟、兼容与可扩展,而它的安全难点也恰恰源于同一处:大量历史机制、动态能力和企业组件,长年累月地叠加在一起。今天想做好 Java 安全,既不能只盯着某一个 CVE,也不能指望靠升级 JDK 就一劳永逸。真正需要管理的,是完整的数据链路、运行时的 classpath、组件之间的解释差异,以及代码执行之后的权限边界。
这也是 Black Hat 二十余年 Java 议题留下的最清楚的一条线索。