蓝易云CDN:java 反序列化漏洞防御中怎么通过黑白名单校验拦截不安全类的加载

蓝易云CDN:Java 反序列化漏洞防御中,如何通过黑白名单校验拦截不安全类的加载

Java 反序列化漏洞的防御,核心不只是识别恶意请求,更重要的是限制程序能够从不可信数据中恢复哪些对象。通过黑名单、白名单及反序列化过滤机制,可以降低危险对象被处理的风险,减少利用第三方组件调用链实施攻击的可能性。

需要注意:Java 反序列化过滤器并不是通用的类加载拦截器,而是在对象反序列化过程中对对象类型及相关资源使用情况进行检查。 因此,防御方案必须结合实际入口、对象结构和运行环境设计。

一、黑名单与白名单有什么区别?

🔍 黑名单机制:禁止已知危险类。

黑名单通过拒绝特定类或包,阻止已知存在风险的对象参与反序列化。这种方式配置相对简单,但存在明显局限:攻击链可能使用其他组件,危险行为也不一定来自已知的恶意类。黑名单遗漏一个关键类,就可能留下防护缺口。

白名单机制:只允许业务需要的类。

白名单仅允许经过审核的业务对象及其必要依赖参与反序列化,其他未明确授权的类型一律拒绝。对于对象结构固定、业务边界明确的系统,这种方式通常更容易形成可验证的安全策略。

两者的区别在于:黑名单主要回答“哪些类不能使用”,白名单则回答“哪些类才允许使用”。

在生产环境中,建议以严格白名单为主要控制手段,黑名单作为补充,而不是依赖不断追加危险类名称来维持安全。

二、如何使用 ObjectInputFilter 实现白名单校验?

Java 提供了 ObjectInputFilter 机制,可以限制原生反序列化过程中允许出现的类,并设置对象深度、引用数量、数组长度和输入流大小等约束。

下面以 Java 9 及以上版本为例,展示基本实现方式:

import java.io.ObjectInputFilter;
import java.io.ObjectInputStream;

ObjectInputFilter filter = info -> {
    // 限制对象图及输入资源消耗
    if (info.depth() > 10
            || info.references() > 1000
            || info.streamBytes() > 1024 * 1024
            || (info.arrayLength() >= 0
                && info.arrayLength() > 10000)) {
        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;
};

try (ObjectInputStream in =
         new ObjectInputStream(inputStream)) {

    // 必须在 readObject() 之前设置过滤器
    in.setObjectInputFilter(filter);

    Object obj = in.readObject();
}

其中,MyRequestDTO 代表业务中明确允许的序列化数据类型,inputStream 是待处理的输入流。

这段代码用于说明过滤机制,不能直接视为适用于所有业务的生产配置。实际使用时,需要根据对象图补充经过审核的必要类型,包括业务对象的字段类型、集合实现类和必要的数组类型等。示例中的资源限制也应结合正常业务数据规模进行调整。

特别注意: UNDECIDED 不代表允许。它表示当前过滤器没有对该对象作出最终判断。如果使用全局过滤器或过滤器工厂组合策略,还需要确认其他过滤规则如何处理这一状态。

三、黑名单如何配置?能否直接禁止危险包?

对于支持 Java 原生反序列化过滤模式的 JDK,可以通过 jdk.serialFilter 等配置设置类过滤规则。例如,以下为过滤规则的示意:

maxdepth=10;maxrefs=1000;maxbytes=1048576;!com.example.unsafe.**;*

这段规则用于说明资源限制与类匹配规则的组合方式:限制对象深度、引用数量和流大小,并拒绝指定包中的类。

但必须明确,末尾的 * 会匹配其他未被前面规则匹配的类,因此这仍然是以黑名单为主的策略,不能等同于严格白名单。对于高风险入口,更合理的做法是明确允许的类,并拒绝所有未匹配类型。

配置前还应核实目标 JDK 版本、过滤规则语法及实际生效范围,不能将某个示例包名直接当作所有危险组件的完整列表。

四、实施黑白名单时,最容易忽略的四个问题

1. 只校验最终对象类型,已经太晚。

如果等到 readObject() 返回后才检查 obj.getClass(),对象恢复过程中的特殊方法可能已经执行。因此,类型限制必须在反序列化期间生效,而不能只依靠反序列化后的类型判断。

2. 只过滤一个危险类,不代表整个对象图安全。

反序列化对象可能包含嵌套对象、集合和其他依赖类型。白名单必须覆盖真实业务所需的完整对象结构,同时拒绝未授权的类型,不能为了让程序运行而简单放行整个包。

3. 白名单也必须配合资源限制。

即使对象类型合法,过深的对象图、过多的对象引用或超大数组仍可能造成资源消耗。因此,应同时限制输入规模、对象深度、引用数量及数组长度。

4. 不同序列化组件需要分别治理。

ObjectInputFilter 主要用于 Java 原生序列化机制,并不会自动保护 JSON 多态反序列化、XML 解析或其他第三方序列化框架。相关组件需要采用各自适用的类型约束与安全配置。

五、蓝易云CDN与源站应该如何协同防护?

对于通过 HTTP/HTTPS 进入源站的 Java 业务,可以在 CDN/WAF 层实施请求频率控制、异常请求识别和访问策略,减少可疑请求到达应用的机会。

但 CDN 无法可靠地替代 Java 应用内部的对象白名单。序列化数据可能经过编码或加密,也可能通过非 HTTP 协议和内部服务调用进入应用。仅依靠请求体中的固定特征进行拦截,既可能漏报,也可能误伤正常业务。

建议形成以下防护体系:

  • 接入层: 使用 CDN/WAF 过滤适用范围内的异常请求,并限制不必要的公网访问。
  • 应用层: 优先避免处理不可信的 Java 原生序列化数据;无法避免时,使用严格类型白名单和资源限制。
  • 组件层: 排查第三方依赖及危险反序列化入口,及时修复已确认的问题。
  • 运行层: 限制应用权限和网络出口,记录异常反序列化、非预期外联及资源消耗。
  • 验证层: 使用授权测试验证过滤器是否生效,并确认正常业务对象仍能正确处理。

六、总结

Java 反序列化安全校验的关键,是在对象恢复过程中限制可接受的类型,而不是仅在攻击发生后检查结果。

优先使用严格白名单,辅以黑名单、对象图资源限制、依赖更新和最小权限策略,才能形成可验证的纵深防御。 对于企业生产环境,还应将 CDN/WAF 作为外围防护,而不是替代应用自身的安全校验机制。

THE END