蓝易云CDN:Java 中反序列化怎么避免反序列化漏洞攻击与安全校验

蓝易云CDN:Java 中如何避免反序列化漏洞攻击与安全校验

Java 反序列化漏洞是企业应用安全中需要重点关注的风险之一。当程序直接处理不可信的序列化数据时,攻击者可能利用对象构造过程中的特殊方法调用,造成业务逻辑异常、拒绝服务,甚至在特定条件下实现远程代码执行(RCE)。

要有效防御此类漏洞,不能只依赖升级 JDK 或部署 WAF,而应从数据入口、对象类型限制、依赖组件和运行环境等多个层面建立防护机制。

一、Java 反序列化漏洞是如何产生的?

Java 序列化可以将对象转换为字节流,反序列化则负责将字节流还原为对象。常见的原生反序列化入口包括 ObjectInputStream.readObject()。

问题在于,反序列化并非简单的数据读取。某些类在对象恢复过程中会执行 readObject()、readResolve() 等特殊方法。如果应用允许不可信数据控制对象类型和对象之间的引用关系,攻击者就可能借助现有依赖组件中的调用链触发非预期行为。

例如,ysoserial 中的 URLDNS 链可以用于探测特定反序列化行为,但 DNS 回连并不等于已经实现远程命令执行。漏洞是否能够造成更严重的影响,取决于实际调用链、依赖版本和运行环境。

二、避免反序列化漏洞的五项关键措施

1. 尽量避免反序列化不可信对象

这是最根本的防御措施。

对于公网 API、用户上传文件、消息队列和外部系统传入的数据,应优先采用具有明确数据结构的 JSON 等格式,并将输入映射到专用数据传输对象(DTO)。

需要注意,JSON 并不天然安全。如果开启了不受限制的多态类型反序列化,或者允许外部数据指定任意类名,仍可能产生安全问题。因此,应关闭不必要的默认类型推断,并严格约束可解析的类型。

2. 使用 ObjectInputFilter 限制反序列化

🛡️ JDK 提供了反序列化过滤机制 ObjectInputFilter,可用于限制允许还原的类,并检查对象图深度、引用数量、数组长度和序列化流大小。

该机制在 JDK 9 引入,部分较早的 JDK 版本也通过更新获得了相关支持。JDK 17 还增强了按上下文选择过滤器的能力。

以下为基础示例,实际使用时应将示例类替换为业务明确需要的类型,并补齐对象图中所有必要的类:

ObjectInputFilter filter = info -> {
    if (info.depth() > 20
            || info.references() > 10000
            || info.streamBytes() > 1048576
            || info.arrayLength() > 100000) {
        return ObjectInputFilter.Status.REJECTED;
    }

    Class<?> type = info.serialClass();

    if (type == null) {
        return ObjectInputFilter.Status.UNDECIDED;
    }

    if (type == MyRequestDTO.class
            || type == String.class) {
        return ObjectInputFilter.Status.ALLOWED;
    }

    return ObjectInputFilter.Status.REJECTED;
};

在创建 ObjectInputStream 后、调用 readObject() 前设置过滤器:

try (ObjectInputStream in =
         new ObjectInputStream(inputStream)) {
    in.setObjectInputFilter(filter);
    Object obj = in.readObject();
}

重要提醒: 上述代码仅用于说明过滤原理,不是可直接套用的完整生产配置。真实业务可能需要允许特定集合、数组和嵌套 DTO;白名单必须经过兼容性测试。还应单独限制输入流总大小,因为过滤器并不能覆盖所有基础数据读取场景。过滤机制默认不会自动启用,必须检查实际配置是否生效。

3. 对反序列化入口实施身份与完整性校验

如果业务必须传输 Java 序列化数据,应优先考虑迁移到更容易验证的数据格式。

确实无法迁移时,可以在反序列化之前验证消息的数字签名或消息认证码(MAC),并确认签名密钥不受客户端控制。必要时还应增加时间戳、有效期或随机数,防止旧消息被重复提交。

但必须明确:签名校验只能证明数据来源或完整性符合预期,不能代替对象类型白名单和资源限制。 即使数据经过认证,错误的对象结构仍可能导致应用异常。

4. 排查第三方组件与危险调用入口

安全审计时,应重点检查以下位置:

  • ObjectInputStream.readObject()、readUnshared() 等原生反序列化调用。
  • 使用外部数据的 XMLDecoder、XStream 等组件。
  • 开启多态类型处理或允许外部指定类型的 JSON 解析配置。
  • RMI、RPC、消息队列以及其他可能传输序列化对象的接口。

应结合实际依赖版本、官方安全公告和调用路径进行评估,及时更新存在风险的组件。不能仅凭项目中存在某个组件,就直接认定系统存在可利用漏洞。

5. 限制运行权限并监控异常行为

将处理不可信数据的服务以最小权限运行,限制文件访问、外部网络连接和系统命令执行能力。对于高风险业务,可以考虑独立进程或容器隔离,并配置合理的 CPU、内存和请求大小限制。

同时,应监控反序列化异常、异常 DNS 请求、非预期外联以及 CPU 和内存使用量的异常变化。监控的作用是辅助发现风险,不能代替代码修复。

三、WAF 和 CDN 能否防御 Java 反序列化攻击?

蓝易云CDN可以作为 Web 业务的前置防护层,对适用范围内的 HTTP/HTTPS 请求实施访问控制、异常流量识别和 WAF 规则检测,降低恶意请求到达源站的概率。

但 Java 反序列化攻击可能使用不同的数据编码、协议和调用入口。仅依靠请求体中的 AC ED 00 05 等序列化标记进行拦截,容易出现漏报或误报;加密数据和非 HTTP 通信也可能超出 CDN 的检测范围。

更合理的方式是:CDN/WAF 前置过滤、Java 应用对象白名单、依赖组件修复、源站访问控制和运行时监控协同工作。

四、生产环境安全检查清单

上线前建议逐项确认:

  1. 是否排查了所有不可信数据进入反序列化流程的路径?
  2. 是否优先使用明确的数据格式和专用 DTO?
  3. 是否配置了严格的类白名单及对象资源限制?
  4. 是否验证过滤器在实际运行环境中生效?
  5. 是否更新了存在风险的依赖组件?
  6. 是否限制了服务权限、网络出口和源站访问?
  7. 是否通过授权测试验证正常业务兼容性及异常输入处理?

五、总结

Java 反序列化漏洞的防御重点,不是简单屏蔽某个特征字符串,而是控制哪些数据可以进入反序列化流程、哪些对象可以被创建,以及对象处理过程能够消耗多少系统资源。

优先避免反序列化不可信对象;无法避免时,采用严格白名单、资源限制和消息完整性校验,并结合依赖更新、最小权限和 CDN/WAF 防护,才能形成更可靠的纵深防御体系。

THE END