浅谈Java反序列化漏洞修复方案
字数 1231 2025-08-29 08:31:41

Java反序列化漏洞原理与修复方案详解

一、序列化与反序列化基础

1.1 序列化概念

序列化是将Java对象转换为字节流的过程,使得对象可以脱离Java运行环境,实现:

  • 多平台通信
  • 对象持久化存储
  • 远程方法调用(RMI)

1.2 反序列化过程

Java程序使用ObjectInputStreamreadObject()方法将字节流反序列化为Java对象。当输入的反序列化数据可被用户控制时,就可能存在安全漏洞。

二、反序列化漏洞原理

2.1 漏洞产生条件

当满足以下条件时,反序列化漏洞可能被利用:

  1. 应用程序接受外部输入的序列化数据
  2. 使用ObjectInputStream.readObject()方法反序列化这些数据
  3. 类路径中包含可利用的"gadget chain"类

2.2 漏洞代码示例

// 读取输入流并转换对象
InputStream in = request.getInputStream();
ObjectInputStream ois = new ObjectInputStream(in);
// 恢复对象
ois.readObject();
ois.close();

2.3 序列化数据结构

Java序列化数据具有特定的格式:

  • 魔术数字:2字节,固定为0xACED
  • 版本号:2字节,通常为0x0005
  • 类描述信息:包括类名、成员变量类型和数量等

示例序列化数据:

00000000: aced 0005 7372 0024 636f 6d2e 7878 7878 ....sr.$com.xxxx
00000010: 7878 2e73 6563 2e77 6562 2e68 6f6d 652e xx.sec.web.home.
00000020: 5365 7269 616c 4f62 6a65 6374 4fda af97 SerialObjectO...

三、反序列化漏洞修复方案

3.1 通过Hook resolveClass校验反序列化的类

实现原理

重写ObjectInputStreamresolveClass方法,在反序列化时首先校验类名。

代码示例

public class AntObjectInputStream extends ObjectInputStream {
    public AntObjectInputStream(InputStream inputStream) throws IOException {
        super(inputStream);
    }

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

优缺点

  • 优点:可灵活设置白名单或黑名单
  • 缺点:黑名单需要持续维护,无法覆盖未公开的利用方法

3.2 使用ValidatingObjectInputStream校验

实现方式

使用Apache Commons IO Serialization包中的ValidatingObjectInputStream类。

代码示例

private static Object deserialize(byte[] buffer) 
    throws IOException, ClassNotFoundException {
    Object obj;
    ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
    ValidatingObjectInputStream ois = new ValidatingObjectInputStream(bais);
    // 只允许反序列化SerialObject class
    ois.accept(SerialObject.class);
    obj = ois.readObject();
    return obj;
}

3.3 使用contrast-rO0防御

实现方式

使用SafeObjectInputStream类实现白名单控制。

代码示例

SafeObjectInputStream in = new SafeObjectInputStream(inputStream, true);
in.addToWhitelist(SerialObject.class);
in.readObject();

3.4 使用ObjectInputFilter(Java 9+)

实现原理

Java 9引入的序列化数据过滤特性,可自定义过滤器。

代码示例

class BikeFilter implements ObjectInputFilter {
    private long maxStreamBytes = 78;
    private long maxDepth = 1;
    private long maxReferences = 1;

    @Override
    public Status checkInput(FilterInfo filterInfo) {
        if (filterInfo.references() < 0 || 
            filterInfo.depth() < 0 || 
            filterInfo.streamBytes() < 0 || 
            filterInfo.references() > maxReferences || 
            filterInfo.depth() > maxDepth || 
            filterInfo.streamBytes() > maxStreamBytes) {
            return Status.REJECTED;
        }
        
        Class<?> clazz = filterInfo.serialClass();
        if (clazz != null) {
            if (SerialObject.class == filterInfo.serialClass()) {
                return Status.ALLOWED;
            } else {
                return Status.REJECTED;
            }
        }
        return Status.UNDECIDED;
    }
}

3.5 黑名单方案

适用场景

  • 作为第三方库提供反序列化接口
  • 无法预知调用方需要反序列化的类

注意事项

  • 需要持续更新黑名单
  • 无法防御未知的利用方式
  • 建议作为辅助防御措施

四、最佳实践建议

  1. 优先使用白名单:白名单方案比黑名单更安全可靠
  2. 升级Java版本:Java 9+提供了更完善的序列化过滤机制
  3. 避免反序列化不可信数据:从根本上消除风险
  4. 使用替代方案:考虑JSON、XML等更安全的序列化格式
  5. 持续监控:关注新的反序列化漏洞利用方式

五、参考资源

  1. NCC Group Whitepaper on Java Deserialization
  2. Java Serialization Specification
  3. OWASP Deserialization Cheat Sheet
  4. SerialKiller项目
  5. contrast-rO0项目
Java反序列化漏洞原理与修复方案详解 一、序列化与反序列化基础 1.1 序列化概念 序列化是将Java对象转换为字节流的过程,使得对象可以脱离Java运行环境,实现: 多平台通信 对象持久化存储 远程方法调用(RMI) 1.2 反序列化过程 Java程序使用 ObjectInputStream 的 readObject() 方法将字节流反序列化为Java对象。当输入的反序列化数据可被用户控制时,就可能存在安全漏洞。 二、反序列化漏洞原理 2.1 漏洞产生条件 当满足以下条件时,反序列化漏洞可能被利用: 应用程序接受外部输入的序列化数据 使用 ObjectInputStream.readObject() 方法反序列化这些数据 类路径中包含可利用的"gadget chain"类 2.2 漏洞代码示例 2.3 序列化数据结构 Java序列化数据具有特定的格式: 魔术数字:2字节,固定为 0xACED 版本号:2字节,通常为 0x0005 类描述信息:包括类名、成员变量类型和数量等 示例序列化数据: 三、反序列化漏洞修复方案 3.1 通过Hook resolveClass校验反序列化的类 实现原理 重写 ObjectInputStream 的 resolveClass 方法,在反序列化时首先校验类名。 代码示例 优缺点 优点 :可灵活设置白名单或黑名单 缺点 :黑名单需要持续维护,无法覆盖未公开的利用方法 3.2 使用ValidatingObjectInputStream校验 实现方式 使用Apache Commons IO Serialization包中的 ValidatingObjectInputStream 类。 代码示例 3.3 使用contrast-rO0防御 实现方式 使用 SafeObjectInputStream 类实现白名单控制。 代码示例 3.4 使用ObjectInputFilter(Java 9+) 实现原理 Java 9引入的序列化数据过滤特性,可自定义过滤器。 代码示例 3.5 黑名单方案 适用场景 作为第三方库提供反序列化接口 无法预知调用方需要反序列化的类 注意事项 需要持续更新黑名单 无法防御未知的利用方式 建议作为辅助防御措施 四、最佳实践建议 优先使用白名单 :白名单方案比黑名单更安全可靠 升级Java版本 :Java 9+提供了更完善的序列化过滤机制 避免反序列化不可信数据 :从根本上消除风险 使用替代方案 :考虑JSON、XML等更安全的序列化格式 持续监控 :关注新的反序列化漏洞利用方式 五、参考资源 NCC Group Whitepaper on Java Deserialization Java Serialization Specification OWASP Deserialization Cheat Sheet SerialKiller项目 contrast-rO0项目