jdk.serialFilter 本质是白名单优先的过滤器,不支持可靠黑名单机制;应严格限定业务必需类、设置资源限制,并配合禁用默认类型、替换序列化方案等多重防护。

jdk.serialFilter 不支持黑名单机制,它本质是白名单优先的过滤器。所谓“黑名单配置”在实践中不可靠、易绕过,且官方设计明确拒绝黑名单逻辑——试图用 !com.sun.* 或 !org.apache.commons.collections.* 这类排除规则来防御,反而会制造虚假安全感。
为什么 jdk.serialFilter 不能真正实现黑名单
过滤规则按顺序匹配,一旦某条规则命中(无论允许还是拒绝),后续规则不再生效。而危险 gadget 类往往来自第三方库(如 Jackson、FastJSON、Commons Collections),这些类名不固定、包路径多样、版本迭代频繁。更关键的是:
- 攻击者可选用未被你“排除”的冷门 gadget 类,比如
sun.reflect.annotation.AnnotationInvocationHandler或javax.management.BadAttributeValueExpException,它们不在常见黑名单里却极常用 - 用
!xxx排除某个包,但 gadget 链可能跨多个包组合触发,单点排除毫无意义 - JDK 自身部分内部类(如
sun.misc.Unsafe相关路径)在低版本中无法被 filter 覆盖,且可通过非标准反序列化路径绕过
正确用法:严格白名单 + 限制资源消耗
应聚焦于“只放行业务必需的类”,配合深度、引用、字节等维度约束:
-
基础包显式放行:如
java.base/*;java.desktop/*,确保 JDK 核心类可用 -
业务模型精准限定:如
com.mycompany.dto.**;com.mycompany.model.**,用**匹配子包,避免漏掉嵌套类 -
全局兜底拒绝:末尾加
;!*(注意分号和空格),确保未声明的类一律拦截 -
附加资源限制:加上
maxdepth=5;maxrefs=100;maxbytes=1000000;maxarray=10000,防内存耗尽或深度嵌套攻击
典型错误配置与风险
以下写法看似合理,实则高危:
-
my.app.**;!*:放行整个业务包,但若引入了依赖库(如com.fasterxml.jackson.databind.*),其内部类也会被一并放行,而这些类常含 gadget -
!*;java.base/*;my.app.**:顺序错误,!*在前直接拒绝全部,后面规则永不执行 -
-Djdk.serialFilter=空值或仅设maxbytes:等价于全放行,filterCheck 不触发校验
补充防线:不能只靠 jdk.serialFilter
该机制只是第一道轻量网关,不是银弹:
- 对重写了
ObjectInputStream.resolveClass()的代码无效——如果业务手动绕过 filter 加载类,配置即失效 - RMI、JMX、HTTP Body、Redis、MQ 等所有接受字节流的地方,都需独立评估是否启用 filter
- 优先替换原生序列化:用 Jackson(禁用
enableDefaultTyping)、Protobuf 或 Avro,从源头消除风险











