Java序列化和反序列化
字数 1277 2025-08-12 11:34:11

Java序列化与反序列化全面解析

1. 序列化与反序列化基础概念

1.1 定义

  • 序列化:将Java对象转换为字节序列的过程
  • 反序列化:将字节序列恢复为Java对象的过程

1.2 核心目的

  • 持久化存储对象
  • 网络传输对象
  • 实现跨JVM的对象传递

2. 序列化机制实现

2.1 必要条件

  • 类必须实现java.io.Serializable接口
  • Serializable是一个标记接口(Marker Interface),不包含任何方法

2.2 核心类

  • ObjectOutputStream:序列化对象
  • ObjectInputStream:反序列化对象

3. 序列化与反序列化操作

3.1 序列化步骤

  1. 创建ObjectOutputStream输出流
  2. 调用writeObject()方法输出可序列化对象
ObjectOutputStream objectOutputStream = new ObjectOutputStream(
    new FileOutputStream("123.ser"));
objectOutputStream.writeObject(serializeDemo);
objectOutputStream.close();

3.2 反序列化步骤

  1. 创建ObjectInputStream输入流
  2. 调用readObject()方法获取序列化对象
ObjectInputStream objectInputStream = new ObjectInputStream(
    new FileInputStream("123.ser"));
SerializeDemo ss = (SerializeDemo) objectInputStream.readObject();

3.3 字节数组操作

序列化到字节数组

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(serializeDemo);
objectOutputStream.close();
byte[] serializedData = byteArrayOutputStream.toByteArray();

从字节数组反序列化

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(serializedData);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
SerializeDemo obj = (SerializeDemo)objectInputStream.readObject();

4. 序列化特征与格式

4.1 序列化数据特征

  • 前32位十六进制特征:
    • AC ED:STREAM_MAGIC,声明使用序列化协议
    • 00 05:STREAM_VERSION,序列化协议版本

4.2 序列化内容

序列化的字节序列包含:

  • 对象的数据
  • 对象的类型信息
  • 对象中数据的类型信息

5. 安全风险与漏洞

5.1 安全隐患

  • 反序列化可以直接从byte[]创建实例,绕过构造方法
  • 精心构造的byte[]数组可能导致任意代码执行

5.2 反序列化漏洞攻击流程

  1. 客户端构造payload并进行多层封装
  2. exp发送到服务端,进入重写的readObject函数
  3. 服务端反序列化恢复恶意数据
  4. 执行流程中层层解析恶意数据
  5. 最终执行任意命令

5.3 漏洞挖掘要点

  • 自定义readObject方法可能包含危险操作
  • 寻找可利用的反序列化链(如commons-collections利用链)
  • 寻找可从外部访问的重写readObject方法

6. 最佳实践与替代方案

6.1 安全建议

  • 避免直接反序列化不可信数据
  • 对序列化数据进行签名或加密
  • 使用白名单机制验证反序列化的类

6.2 替代方案

  • 使用JSON等通用序列化格式
  • 仅序列化基本类型和String内容
  • 避免序列化与代码相关的信息

7. 示例代码

7.1 完整序列化/反序列化示例

import java.io.*;

public class SerializationExample {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // 序列化
        SerializeDemo original = new SerializeDemo();
        original.x = 666;
        
        try (ObjectOutputStream out = new ObjectOutputStream(
                new FileOutputStream("demo.ser"))) {
            out.writeObject(original);
        }
        
        // 反序列化
        try (ObjectInputStream in = new ObjectInputStream(
                new FileInputStream("demo.ser"))) {
            SerializeDemo restored = (SerializeDemo) in.readObject();
            System.out.println("Restored value: " + restored.x);
            System.out.println("Method result: " + restored.add(1, 2));
        }
    }
}

class SerializeDemo implements Serializable {
    public int x;
    
    public int add(int a, int b) {
        return a + b + x;
    }
}

7.2 自定义readObject示例(潜在风险)

import java.io.*;

class VulnerableClass implements Serializable {
    private String command;
    
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        // 危险操作:执行反序列化数据中的命令
        Runtime.getRuntime().exec(command);
    }
    
    // 其他代码...
}

8. 总结要点

  1. 序列化机制允许对象在JVM关闭后仍能保存和传输
  2. 必须实现Serializable接口才能序列化
  3. 序列化数据包含对象完整信息,包括类型和数据
  4. 反序列化不调用构造方法,直接从字节流创建对象
  5. 序列化数据有特定头部特征(AC ED 00 05)
  6. 反序列化不可信数据存在严重安全风险
  7. 自定义readObject方法可能引入漏洞
  8. 考虑使用JSON等更安全的替代方案

通过理解这些核心概念和注意事项,开发者可以安全有效地使用Java序列化机制,同时避免潜在的安全风险。

Java序列化与反序列化全面解析 1. 序列化与反序列化基础概念 1.1 定义 序列化 :将Java对象转换为字节序列的过程 反序列化 :将字节序列恢复为Java对象的过程 1.2 核心目的 持久化存储对象 网络传输对象 实现跨JVM的对象传递 2. 序列化机制实现 2.1 必要条件 类必须实现 java.io.Serializable 接口 Serializable 是一个标记接口(Marker Interface),不包含任何方法 2.2 核心类 ObjectOutputStream :序列化对象 ObjectInputStream :反序列化对象 3. 序列化与反序列化操作 3.1 序列化步骤 创建 ObjectOutputStream 输出流 调用 writeObject() 方法输出可序列化对象 3.2 反序列化步骤 创建 ObjectInputStream 输入流 调用 readObject() 方法获取序列化对象 3.3 字节数组操作 序列化到字节数组 从字节数组反序列化 4. 序列化特征与格式 4.1 序列化数据特征 前32位十六进制特征: AC ED :STREAM_ MAGIC,声明使用序列化协议 00 05 :STREAM_ VERSION,序列化协议版本 4.2 序列化内容 序列化的字节序列包含: 对象的数据 对象的类型信息 对象中数据的类型信息 5. 安全风险与漏洞 5.1 安全隐患 反序列化可以直接从byte[ ]创建实例,绕过构造方法 精心构造的byte[ ]数组可能导致任意代码执行 5.2 反序列化漏洞攻击流程 客户端构造payload并进行多层封装 exp发送到服务端,进入重写的readObject函数 服务端反序列化恢复恶意数据 执行流程中层层解析恶意数据 最终执行任意命令 5.3 漏洞挖掘要点 自定义 readObject 方法可能包含危险操作 寻找可利用的反序列化链(如commons-collections利用链) 寻找可从外部访问的重写 readObject 方法 6. 最佳实践与替代方案 6.1 安全建议 避免直接反序列化不可信数据 对序列化数据进行签名或加密 使用白名单机制验证反序列化的类 6.2 替代方案 使用JSON等通用序列化格式 仅序列化基本类型和String内容 避免序列化与代码相关的信息 7. 示例代码 7.1 完整序列化/反序列化示例 7.2 自定义readObject示例(潜在风险) 8. 总结要点 序列化机制允许对象在JVM关闭后仍能保存和传输 必须实现Serializable接口才能序列化 序列化数据包含对象完整信息,包括类型和数据 反序列化不调用构造方法,直接从字节流创建对象 序列化数据有特定头部特征(AC ED 00 05) 反序列化不可信数据存在严重安全风险 自定义readObject方法可能引入漏洞 考虑使用JSON等更安全的替代方案 通过理解这些核心概念和注意事项,开发者可以安全有效地使用Java序列化机制,同时避免潜在的安全风险。