XMLDecoder不能读取不信任的XML数据,因为它会直接执行XML中声明的任意Java类构造、方法调用和字段赋值,等同于执行不受控的Java代码,极易被利用执行恶意操作。

XMLDecoder 为什么不能读不信任的 XML 数据
因为 XMLDecoder 会直接执行 XML 中声明的任意 Java 类构造、方法调用和字段赋值,等同于在 JVM 里跑了一段不受控的 Java 代码。只要攻击者能控制输入 XML,就能调用 Runtime.getRuntime().exec()、写文件、加载恶意类——漏洞利用门槛极低,且无需任何依赖。
常见错误现象:java.lang.ClassNotFoundException 看似是类找不到,实际可能是攻击者故意触发类加载失败来探测环境;更危险的是静默执行成功,连日志都不留。
- 使用场景:旧系统中用
XMLDecoder做配置反序列化、UI 组件状态恢复、测试数据加载 - 参数差异:它不校验类名白名单,也不限制方法调用,
setAccessible(true)和私有字段赋值默认全开 - 兼容性影响:JDK 8u121+ 默认禁用部分高危类(如
ProcessBuilder),但绕过方式公开且简单,不能依赖这个“防护”
替代方案:用 JAXB 或 Jackson XML 替掉 XMLDecoder
JAXB(javax.xml.bind.JAXBContext)和 Jackson XML(XmlMapper)都只做字段映射,不执行逻辑,前提是不用 @XmlJavaTypeAdapter 引入自定义反序列化器。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 优先选
XmlMapper:Jackson 对类型安全控制更细,可设DefaultTyping.NON_FINAL关闭自动类型识别 - 禁用 JAXB 的
setEventHandler和setSchema外部实体加载(防 XXE) - 所有目标类必须有无参构造函数 + public setter,否则解析失败而非执行任意代码
- 示例:用
XmlMapper读取配置时,明确指定类型mapper.readValue(xmlBytes, Config.class),不传Object.class
如果必须保留 XMLDecoder,至少加三道隔离
这不是推荐做法,而是给无法立即下线的老系统兜底。核心思路是切断它接触真实类路径、系统资源和反射能力的通路。
- 用自定义
ClassLoader加载XMLDecoder实例,只允许加载白名单里的几个基础类(如java.lang.String、java.util.ArrayList) - 在
SecurityManager策略中禁止RuntimePermission("createClassLoader")和FilePermission,JDK 17+ 需改用System.setSecurityManager(null)后自行拦截(因 SecurityManager 已废弃但未移除) - 预处理 XML:用正则或 SAX 解析器先扫描是否含
<object class=、<method name=、<void method=,命中即拒收——注意别漏掉换行/空格绕过(如<void<space>method=)
最容易被忽略的点:XMLDecoder 常藏在第三方库底层
很多老 UI 框架(如 Apache Pivot)、报表工具(JasperReports 早期版本)或序列化工具封装了 XMLDecoder,你没直接写它,但它在运行时被调用。查法很简单:
- 启动时加
-verbose:class,搜XMLDecoder类加载记录 - 用
jstack抓到正在运行的线程栈,看是否有XMLDecoder.readObject调用链 - 检查
META-INF/MANIFEST.MF里有没有Implementation-Title: JavaBeans相关描述
修复时别只改自己写的代码,得确认依赖树里所有 jar 是否已升级到修复版本——比如 JasperReports 6.0.0+ 才彻底弃用 XMLDecoder。










