Java原生反序列化天然不安全,因它无条件信任输入字节流,允许动态加载任意类、自动调用readObject等钩子方法、绕过构造器初始化对象,并还原完整对象图,从而被攻击者利用执行任意代码;ObjectInputFilter是Java 9引入的过滤机制,通过白名单类名、限制图深度/数组长度等方式在解析前拦截恶意载荷,但需正确配置且无法防御合法类滥用或业务逻辑漏洞。

Java 的 Serializable 接口本身不安全,问题不在“能否序列化”,而在于反序列化时无条件信任输入数据。攻击者一旦控制字节流,就能触发恶意类的静态块、readObject()、readResolve() 等钩子方法,进而执行任意代码。自 Java 9 起引入的 ObjectInputFilter 是官方提供的第一道可控防线,但它不是银弹,需正确配置才能起效。
为什么 Serializable 天然存在风险?
Java 原生序列化机制设计目标是“完全还原对象状态”,为此它允许:
- 动态加载任意类(包括攻击者打包进 payload 的恶意类)
- 自动调用敏感生命周期方法(如
readObject()、readExternal()、__wakeup()类似逻辑) - 绕过构造函数直接分配对象内存并填充字段(包括 private 和 final 字段)
- 还原对象图中所有引用,可能触发深层链式调用(如 Commons Collections 利用链)
这些能力在可信环境里提升效率,但在处理用户输入、网络请求、缓存数据等场景下,就成了可被利用的“后门”。
ObjectInputFilter 是什么?它怎么工作?
ObjectInputFilter 是 Java 9 引入的接口,用于在反序列化前对即将加载的类、数组长度、图深度、引用数量等进行策略性拦截。它不解析或校验对象内容,而是在 ObjectInputStream 解析字节流过程中实时决策是否放行。
关键控制点包括:
-
类名白名单/黑名单:例如只允许
java.lang.String、com.example.User,拒绝java.util.HashMap或所有org.apache.commons.collections.* - 数组最大长度:防止超大数组导致内存耗尽(DoS)
- 对象图深度限制:避免嵌套过深引发栈溢出或性能崩溃
- 总引用数上限:防御循环引用或巨型对象图攻击
它通过 ObjectInputStream.setObjectInputFilter() 设置,也可通过 JVM 启动参数全局配置(如 -Djdk.serialFilter=... )。
如何正确启用和配置 ObjectInputFilter?
仅声明接口没用,必须主动安装并配置合理规则:
-
优先使用白名单模式:明确列出允许反序列化的类及其包路径,例如
maxdepth=5;maxarray=100000;module java.base;module com.myapp.core;!* -
禁用通配符泛滥:避免写成
com.myapp.**,应精确到具体类或稳定子包 - 与业务逻辑解耦:不要把 filter 逻辑硬编码在反序列化调用处,建议统一初始化、集中管理
-
注意作用域覆盖:filter 对当前
ObjectInputStream实例生效;若使用框架(如 RMI、JMX),需确认其底层是否支持并启用了该 filter -
配合日志审计:开启
jdk.serialFilter.log参数记录拦截事件,便于发现异常尝试
ObjectInputFilter 的局限性要清楚
它解决的是“不该加载的类别被加载”问题,但无法防范:
- 已存在于 classpath 中的合法类被滥用(如
java.lang.Runtime+ 反射链) - 业务类自身存在危险的
readObject()实现(比如未校验字段值就执行命令) - filter 规则配置宽松或被绕过(如用
java.lang.Object替代具体类型) - 非 Java 原生序列化格式(如 JSON、XML)的反序列化漏洞(那是 Jackson / XStream 等库的事)
因此,真正安全的方案是:禁用原生序列化 → 改用 JSON/YAML/Protobuf 等文本或结构化格式 → 若必须用 Java 序列化,则强制启用 filter + 白名单 + 定期审计反序列化入口点。

















