反序列化必须强制使用白名单机制,JDK 9+ 推荐 ObjectInputFilter 静态配置,规则需在 new ObjectInputStream() 后、readObject() 前设置,支持包匹配(与*)、深度/引用/数组限制;JDK 8 或需动态控制时可重写 resolveClass;严禁开放高危包、规避正则校验、遗漏数组及特殊类;配套需审查白名单类的 readObject 逻辑并禁用敏感操作。

核心是只允许业务明确需要且确认安全的类参与反序列化,其他一律拒绝。白名单不是可选项,而是必须项——默认放行等于直接暴露攻击面。
用 ObjectInputFilter 设置静态白名单规则
JDK 9+ 原生支持,最轻量也最可靠。关键在规则写法和绑定时机:
- 必须在 new ObjectInputStream() 之后、调用 readObject() 之前设置,晚了就失效
- 用 ObjectInputFilter.Config.createFilter("规则字符串") 创建,不能传 null
- 通配符 * 只匹配当前包一级,子包要用 **;例如 com.example.dto.* 不包含 com.example.dto.sub.User,得写成 com.example.dto.**
- 规则顺序影响优先级:允许项放前面,兜底拒绝用 !* 放最后
- 建议加上基础防护参数:maxdepth=5(限制嵌套深度)、maxrefs=100(防循环引用)、maxarray=50000(控数组大小)
示例:ObjectInputStream ois = new ObjectInputStream(inputStream);
ois.setObjectInputFilter(ObjectInputFilter.Config.createFilter("java.lang.String;java.util.ArrayList;com.example.model.**;maxdepth=5;maxrefs=100;maxarray=50000"));
重写 resolveClass 实现运行时白名单校验
适用于 JDK 8 或需动态控制的场景,比如按租户切换白名单、或集成审计日志:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
- 继承 ObjectInputStream,重写 protected Class<?> resolveClass(ObjectStreamClass desc)
- 从 desc.getName() 获取类名,检查是否在预定义 Set<String> 白名单中
- 不匹配就抛 InvalidClassException,不要 fallback 到父类逻辑
- 避免调用 Class.forName(),仅用字符串判断,防止意外触发类加载
- 白名单集合建议用 Set.of() 或 ConcurrentHashMap 缓存,提升查找效率
避免白名单被绕过的常见陷阱
白名单本身若配置不当,可能形同虚设:
- 别开放 java.**、javax.**、org.apache.commons.collections.** 等高危包路径
- 不依赖正则表达式做类名校验,容易被 Unicode 或编码绕过
- 全局 JVM 参数 -Djdk.serialFilter 是补充,不能替代代码中显式设置
- 数组类型如 [Ljava.lang.String; 需单独列入白名单,或确保其组件类型已允许
- record 类、匿名内部类、Lambda 生成类通常不在白名单内,应禁止反序列化
配套加固:不只是拦类,还要断执行链
白名单拦住非法类,但合法类里的 readObject() 仍可能执行危险操作:
- 对白名单内的类,审查其自定义反序列化逻辑,禁用 Runtime.exec、ProcessBuilder、JNDI lookup 等敏感调用
- Java 17+ 已移除 SecurityManager,如需拦截系统调用,可用字节码插桩(如 Byte Buddy)在方法入口加检查
- 对已知高危 gadget 类(如 TemplatesImpl、BadAttributeValueExpException),即使不在白名单里,也要在过滤器中显式 deny
- 生产环境应关闭所有非必要反序列化入口,比如 HTTP 接口、MQ 消费者、RMI 服务端

















