如何防止 Java 反序列化集合类对象带来的安全漏洞?

文章导读
最稳妥的方案是彻底避免使用 Java 原生序列化机制处理外部不可信数据。如果业务无法避免,必须实施严格的类白名单校验,并配合依赖包版本加固。
📋 目录
  1. 完整代码实现示例
  2. 依赖包版本加固
  3. 分步处理与验证
  4. 常见坑与风险警示
A A

最稳妥的方案是彻底避免使用 Java 原生序列化机制处理外部不可信数据。如果业务无法避免,必须实施严格的类白名单校验,并配合依赖包版本加固。

先说结论:Java 原生反序列化风险极高,除非内部可信通道,否则不应直接接收外部序列化对象。

如何防止 Java 反序列化集合类对象带来的安全漏洞?
  • 先判断:确认业务是否真的必须使用 Java 原生序列化协议传输数据,优先替换为 JSON。
  • 优先做:若必须使用,立即实施类白名单过滤,自定义 ObjectInputStream 子类。
  • 再验证:通过漏洞扫描工具或手动构造测试数据确认防护生效,内网接口同样需要防护。

完整代码实现示例

以下是一个完整的白名单校验输入流实现,包含白名单初始化逻辑。请根据实际业务包名调整 whitelist 内容。

如何防止 Java 反序列化集合类对象带来的安全漏洞?
import java.io.*;
import java.util.*;

public class ValidatingObjectInputStream extends ObjectInputStream {
    private static final Set<String> WHITELIST = new HashSet<>();

    static {
        // 初始化白名单,仅允许必要的业务类和基础类
        WHITELIST.add("com.example.business.User");
        WHITELIST.add("java.lang.String");
        WHITELIST.add("java.util.ArrayList");
        // 禁止所有危险集合类 gadget,除非业务强依赖且已加固
        // WHITELIST.add("org.apache.commons.collections4.functors.InvokerTransformer"); 
    }

    public ValidatingObjectInputStream(InputStream in) throws IOException {
        super(in);
    }

    @Override
    protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
        if (!WHITELIST.contains(desc.getName())) {
            throw new InvalidClassException("Unauthorized deserialization attempt: " + desc.getName());
        }
        return super.resolveClass(desc);
    }
}

// 使用示例
// try (ValidatingObjectInputStream ois = new ValidatingObjectInputStream(inputStream)) {
//     Object obj = ois.readObject();
// } catch (Exception e) {
//     logger.error("Deserialization failed", e);
// }

依赖包版本加固

许多反序列化漏洞源于第三方库中的 Gadget Chain。请检查并升级以下常见高危依赖:

如何防止 Java 反序列化集合类对象带来的安全漏洞?
  • commons-collections: 升级至 3.2.2+ 或推荐使用 commons-collections4 4.4+ 及以上版本。
  • commons-beanutils: 升级至 1.9.4+ 及以上版本。
  • groovy: 升级至 2.4.8+ 及以上版本。

建议使用 OWASP Dependency-Check 定期扫描项目依赖:

./dependency-check.sh `--scan` ./path/to/your/project `--format` HTML

分步处理与验证

  1. 排查入口:全局搜索 ObjectInputStreamXMLDecoderreadObject 等关键词,定位所有反序列化点。
  2. 实施白名单:部署上述 ValidatingObjectInputStream,确保默认拒绝所有未显式允许的类。
  3. 网络层防护:在 WAF 或 RASP 层面开启反序列化攻击检测规则,作为最后一道防线。
  4. 验证生效:在测试环境使用安全的 PoC 工具尝试发送恶意序列化数据,确认服务端抛出 InvalidClassException 且无命令执行。

常见坑与风险警示

  • 白名单维护成本:业务迭代新增类时忘记更新白名单会导致功能报错。建议建立自动化测试流程,在 CI/CD 阶段验证白名单覆盖度。
  • 内网零信任原则:严禁认为内网接口不需要防护。内网横向移动风险同样存在,所有反序列化入口必须实施同等强度的校验。
  • 框架默认配置:部分旧版框架(如某些 RPC 框架)默认开启不安全反序列化,需查阅框架文档手动关闭或升级版本。
  • 继承链风险:白名单校验仅针对类名,若允许的类中存在不安全方法,仍可能被利用。尽量只允许 DTO 等纯数据类。