ObjectInputStream 默认不校验类,反序列化即执行任意代码;JDK 9+ 必须显式设置 ObjectInputFilter,null 值等于全放行;低于 JDK 9 或需动态白名单时应重写 resolveClass();全局 jdk.serialFilter 易被绕过,不可单独依赖。
ObjectInputStream 默认不做类校验,直接反序列化等于执行任意代码
只要攻击者能控制输入流(比如 HTTP 请求体、Redis 缓存值、MQ 消息),
就会按字节流里的类名动态加载类,并调用其
方法——而很多类(如
、
)的该方法内部会触发反射、命令执行或表达式解析。这不是 bug,是 Java 序列化机制的设计本质:
反序列化即执行
。
JDK 9+ 必须显式设置 ObjectInputFilter,null 值 = 全放行
是 JDK 9 引入的 JEP 290 机制,但它默认为
,等同于没开。必须在构造
实例后、调用
前,用
绑定过滤器,晚了就无效。
推荐用
构建规则字符串,例如:
通配符
不匹配子包,要写
才覆盖嵌套路径
规则顺序重要:
放最后,
放前面;以
开头的规则优先级更高
数组长度、对象图深度、引用次数等限制能防 DoS,但不能替代类白名单
低于 JDK 9 或需动态判断时,必须重写 resolveClass()
JDK 8 及更早版本没有
,且某些场景(如多租户系统需按租户切换白名单)必须动态校验。这时唯一可靠方式是继承
并重写
。
CentOS Stream 9
CentOS Stream 9是基于RHEL 9技术路线的持续交付版本,适合需要贴近RHEL 9生态的软件开发、系统集成和测试环境。它相比传统CentOS Linux更靠近上游开发过程,用户可以更早看到RHEL 9后续小版本中的软件包变化。CentOS Stream 9仍是当前可用的官方版本线之一,适合对稳定性和新功能之间有平衡需求的团队使用。
下载
不能只靠
前检查——类加载发生在
内部
从
取类名用
,别用
触发提前加载
白名单建议用
存储,
查找,避免正则匹配带来的性能抖动
非法类名必须抛
,抛
可能被绕过
全局 jdk.serialFilter 参数容易被绕过,不建议单独依赖
通过 JVM 参数
或
设置全局过滤器,看似省事,但有硬伤:
对已重写
的子类无效——代码里手动调用
就绕过了 filter
无法区分不同业务上下文(比如管理后台和用户接口需要不同白名单)
老项目常混用
、
等非标准反序列化路径,这些不走
某些 gadget 链在字段反序列化完成后才触发(如
),此时 filter 早已退出
真正关键的点是:过滤必须发生在类加载那一刻,且不能被子类逻辑绕过;白名单粒度要到具体类名或精确包路径,模糊规则(如
)等于敞开大门。
ObjectInputStreamreadObject()AnnotationInvocationHandlerBadAttributeValueExpExceptionObjectInputFilternullObjectInputStreamreadObject()setObjectInputFilter()ObjectInputFilter.Config.createFilter()"com.myapp.dto.*;maxdepth=5;maxrefs=100;maxarray=50000"*com.myapp.**!*com.myapp.**!ObjectInputFilterObjectInputStreamresolveClass()readObject()resolveClass()ObjectStreamClass descdesc.getName()Class.forName()Setcontains()ClassNotFoundExceptionInvalidClassException-Djdk.serialFilterSystem.setProperty("jdk.serialFilter", "...")resolveClass()super.resolveClass()sun.misc.UnsafeAtomicReferenceFieldUpdaterfilterCheck()InvokerTransformerjava.util.*