java 序列化过程浅析
字数 1881 2025-08-12 11:34:11

Java序列化与反序列化深度解析

一、序列化基础概念

1.1 定义

  • 序列化:将数据结构或对象转换为字节流的过程,以便存储或传输
  • 反序列化:将字节流还原为原始数据结构或对象的过程

1.2 序列化目的

  • 网络传输:RMI、Web Services等远程通信
  • 持久化存储:数据库、文件系统、缓存
  • 认证令牌:如Shiro的RememberMe功能

1.3 Java序列化接口

  • Serializable:标记接口,无方法,表示类可被序列化
  • Externalizable:继承自Serializable,需要手动实现序列化逻辑

二、Java序列化实现

2.1 基本序列化示例

// 可序列化类
public class Operation implements Serializable {
    private final int a;
    private final int b;
    
    public Operation(int a, int b) {
        this.a = a;
        this.b = b;
    }
    
    public void add() {
        System.out.println(a + "+" + b + "=" + (a+b));
    }
}

// 序列化操作类
public class SerializationSample {
    public static void serialize(Object obj, String path) throws IOException {
        FileOutputStream fos = new FileOutputStream(path);
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(obj);
        oos.close();
    }
    
    public static Object deserialize(String path) 
        throws IOException, ClassNotFoundException {
        FileInputStream fis = new FileInputStream(path);
        ObjectInputStream ois = new ObjectInputStream(fis);
        Object obj = ois.readObject();
        ois.close();
        return obj;
    }
}

2.2 序列化数据格式

Java序列化流特征:

  • 魔数:aced(Base64特征为rO0AB
  • 版本号:0005

序列化数据组成结构:

  1. 魔数与版本号
  2. 类描述信息
  3. 字段数据
  4. 结束标记

2.3 SerialVersionUID

  • 8字节长的类指纹信息,决定反序列化是否成功
  • 未显式指定时,JVM会根据类信息自动生成
  • 实际开发中建议显式声明,避免类变更导致兼容性问题

三、高级序列化特性

3.1 自定义序列化

通过重写以下方法实现自定义序列化逻辑:

// 在可序列化类中定义
private void writeObject(ObjectOutputStream out) throws IOException {
    // 自定义序列化逻辑
    out.defaultWriteObject(); // 默认序列化
}

private void readObject(ObjectInputStream in) 
    throws IOException, ClassNotFoundException {
    // 自定义反序列化逻辑
    in.defaultReadObject(); // 默认反序列化
}

3.2 对象替换

// 序列化前替换对象
private Object writeReplace() {
    return replacementObject;
}

// 反序列化后替换对象
private Object readResolve() {
    return replacementObject;
}

注意:

  • writeReplacereadResolve通常不同时使用
  • writeReplace优先级高于readResolve

四、序列化安全与漏洞

4.1 反序列化漏洞原理

当重写readObject方法并执行危险操作时:

private void readObject(ObjectInputStream ois) 
    throws IOException, ClassNotFoundException {
    ois.defaultReadObject();
    // 危险操作示例:动态加载字节码
    Method defineMethod = ClassLoader.class.getDeclaredMethod(
        "defineClass", String.class, byte[].class, int.class, int.class);
    defineMethod.setAccessible(true);
    Class exp = (Class) defineMethod.invoke(
        ClassLoader.getSystemClassLoader(), 
        null, this.byteCode, 0, this.byteCode.length);
    exp.newInstance();
}

4.2 常见反序列化触发点

类型 触发点 所在组件
JDK内置 ObjectInputStream.readObject java.io.ObjectInputStream
JDK内置 ObjectInputStream.readUnshared java.io.ObjectInputStream
XML XMLDecoder.readObject java.beans.XMLDecoder
JSON ObjectMapper.readValue com.fasterxml.jackson.databind.ObjectMapper
JSON JSON.parseObject com.alibaba.fastjson.JSONObject
YAML Yaml.load org.yaml.snakeyaml.Yaml

五、核心API解析

5.1 ObjectOutputStream

  1. writeObject(Object obj)

    • 公开方法,序列化入口
    • 最终调用writeObject0(obj, false)
  2. writeUnshared(Object obj)

    • 与writeObject区别:unshared=true
    • 相同对象不会共享引用,会完整序列化
  3. writeObjectOverride(Object obj)

    • 受保护方法,用于子类覆盖默认序列化行为
  4. writeObject0(Object obj, boolean unshared)

    • 私有方法,实际序列化实现
    • 处理流程:
      • 检查null对象
      • 处理替换对象(writeReplace)
      • 根据对象类型选择序列化方式

5.2 序列化调用链

writeObject 
-> writeObject0 
-> writeOrdinaryObject 
-> writeSerialData 
-> ObjectStreamClass.invokeWriteObject (通过反射调用自定义writeObject)

5.3 ObjectInputStream

与ObjectOutputStream对称,主要方法:

  • readObject()
  • readUnshared()
  • readObjectOverride()
  • readObject0()

六、防御与最佳实践

  1. 避免反序列化不可信数据
  2. 使用白名单验证反序列化类
  3. 重写readObject进行输入验证
  4. 使用transient关键字保护敏感字段
  5. 考虑使用替代序列化方案
    • JSON (Jackson, FastJson)
    • XML
    • Protocol Buffers
    • Kryo

七、扩展学习方向

  1. Java反序列化漏洞利用链(CC链)分析
  2. 各组件反序列化机制深入研究
    • XMLDecoder
    • XStream
    • SnakeYAML
    • Jackson
    • FastJson
  3. 安全防护机制
    • JEP 290(JDK9+)
    • 反序列化过滤器

附录:参考资料

  1. Java序列化规范
  2. Java ObjectOutputStream文档
  3. Java反序列化漏洞原理
  4. Java序列化协议分析
  5. 安全编程指南
Java序列化与反序列化深度解析 一、序列化基础概念 1.1 定义 序列化 :将数据结构或对象转换为字节流的过程,以便存储或传输 反序列化 :将字节流还原为原始数据结构或对象的过程 1.2 序列化目的 网络传输:RMI、Web Services等远程通信 持久化存储:数据库、文件系统、缓存 认证令牌:如Shiro的RememberMe功能 1.3 Java序列化接口 Serializable :标记接口,无方法,表示类可被序列化 Externalizable :继承自Serializable,需要手动实现序列化逻辑 二、Java序列化实现 2.1 基本序列化示例 2.2 序列化数据格式 Java序列化流特征: 魔数: aced (Base64特征为 rO0AB ) 版本号: 0005 序列化数据组成结构: 魔数与版本号 类描述信息 字段数据 结束标记 2.3 SerialVersionUID 8字节长的类指纹信息,决定反序列化是否成功 未显式指定时,JVM会根据类信息自动生成 实际开发中建议显式声明,避免类变更导致兼容性问题 三、高级序列化特性 3.1 自定义序列化 通过重写以下方法实现自定义序列化逻辑: 3.2 对象替换 注意: writeReplace 和 readResolve 通常不同时使用 writeReplace 优先级高于 readResolve 四、序列化安全与漏洞 4.1 反序列化漏洞原理 当重写 readObject 方法并执行危险操作时: 4.2 常见反序列化触发点 | 类型 | 触发点 | 所在组件 | |------|--------|----------| | JDK内置 | ObjectInputStream.readObject | java.io.ObjectInputStream | | JDK内置 | ObjectInputStream.readUnshared | java.io.ObjectInputStream | | XML | XMLDecoder.readObject | java.beans.XMLDecoder | | JSON | ObjectMapper.readValue | com.fasterxml.jackson.databind.ObjectMapper | | JSON | JSON.parseObject | com.alibaba.fastjson.JSONObject | | YAML | Yaml.load | org.yaml.snakeyaml.Yaml | 五、核心API解析 5.1 ObjectOutputStream writeObject(Object obj) 公开方法,序列化入口 最终调用writeObject0(obj, false) writeUnshared(Object obj) 与writeObject区别:unshared=true 相同对象不会共享引用,会完整序列化 writeObjectOverride(Object obj) 受保护方法,用于子类覆盖默认序列化行为 writeObject0(Object obj, boolean unshared) 私有方法,实际序列化实现 处理流程: 检查null对象 处理替换对象(writeReplace) 根据对象类型选择序列化方式 5.2 序列化调用链 5.3 ObjectInputStream 与ObjectOutputStream对称,主要方法: readObject() readUnshared() readObjectOverride() readObject0() 六、防御与最佳实践 避免反序列化不可信数据 使用白名单验证反序列化类 重写readObject进行输入验证 使用transient关键字保护敏感字段 考虑使用替代序列化方案 : JSON (Jackson, FastJson) XML Protocol Buffers Kryo 七、扩展学习方向 Java反序列化漏洞利用链(CC链)分析 各组件反序列化机制深入研究 : XMLDecoder XStream SnakeYAML Jackson FastJson 安全防护机制 : JEP 290(JDK9+) 反序列化过滤器 附录:参考资料 Java序列化规范 Java ObjectOutputStream文档 Java反序列化漏洞原理 Java序列化协议分析 安全编程指南