防御java反序列化dos攻击需在解析前设限:用jdk 9+ objectinputfilter设定maxdepth=5、maxrefs=1000、maxarray=100000等硬上限;辅以白名单类拦截、字节流前置校验,并最终迁移到json/protobuf等安全协议。

Java 反序列化时遭遇超大对象图,本质是攻击者构造深度嵌套、海量引用或巨型数组的序列化数据,让 JVM 在还原过程中内存暴涨、GC 频繁甚至直接 OOM。防御核心不是等反序列化完成再判断,而是在解析过程中就掐断资源失控路径。
用 JDK 9+ 的 ObjectInputFilter 设定硬性资源上限
这是最直接有效的第一道防线。必须显式设置过滤器(null 表示完全放行),且需在 readObject() 调用前绑定:
-
限制嵌套深度:
maxdepth=5拒绝超过 5 层的对象引用链,阻断深层递归结构 -
控制总引用数:
maxrefs=1000防止循环引用或冗余节点拖垮堆内存和 CPU -
约束数组大小:
maxarray=100000避免new byte[Integer.MAX_VALUE]类恶意分配 -
规则顺序关键:白名单类(如
com.myapp.dto.**)写在前面,末尾加!*拦截所有未明确允许的类
继承 ObjectInputStream 自定义 resolveClass 做类级拦截
过滤器不能替代类白名单,尤其在 JDK 8 或需按业务动态控制时,必须重写 resolveClass:
- 从
ObjectStreamClass desc中取getName(),别调Class.forName()提前加载 - 用
Set<string></string>存白名单,contains()查找,避免正则匹配开销 - 非法类名必须抛
ClassNotFoundException,抛InvalidClassException可能被绕过 - 可结合租户 ID、接口路径等上下文动态决定允许哪些类,比全局
jdk.serialFilter更精准
字节流层面做轻量前置校验
在交给 ObjectInputStream 之前,对原始字节流做简单扫描,识别高风险特征:
- 检查序列化流头是否合法(
AC ED 00 05),排除伪造格式 - 统计 TC_REFERENCE、TC_ARRAY 等标记出现频次,发现异常密集引用即拒绝
- 检测是否存在明显循环引用模式(如连续多个相同类描述符索引)
- 估算预期对象数量上限,超出阈值直接中断,不进入反序列化流程
从根本上规避:替换序列化协议
Java 原生序列化机制设计上就难以彻底防住 DoS,长期方案是弃用:
- 对外交互统一用 JSON(Jackson/Fastjson),配合
@JsonCreator和不可变构造器,禁用多态类型(@class) - 内部高性能场景用 Protobuf 或 Avro,它们有严格的 schema 约束,天然拒绝非法结构
- 若必须保留二进制兼容,可用 Kryo 并开启
setRegistrationRequired(true)强制注册类 - 所有反序列化入口加监控:记录耗时、对象数量、最大深度,异常时告警并熔断
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











