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 序列化步骤
- 创建
ObjectOutputStream输出流 - 调用
writeObject()方法输出可序列化对象
ObjectOutputStream objectOutputStream = new ObjectOutputStream(
new FileOutputStream("123.ser"));
objectOutputStream.writeObject(serializeDemo);
objectOutputStream.close();
3.2 反序列化步骤
- 创建
ObjectInputStream输入流 - 调用
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 反序列化漏洞攻击流程
- 客户端构造payload并进行多层封装
- exp发送到服务端,进入重写的readObject函数
- 服务端反序列化恢复恶意数据
- 执行流程中层层解析恶意数据
- 最终执行任意命令
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. 总结要点
- 序列化机制允许对象在JVM关闭后仍能保存和传输
- 必须实现Serializable接口才能序列化
- 序列化数据包含对象完整信息,包括类型和数据
- 反序列化不调用构造方法,直接从字节流创建对象
- 序列化数据有特定头部特征(AC ED 00 05)
- 反序列化不可信数据存在严重安全风险
- 自定义readObject方法可能引入漏洞
- 考虑使用JSON等更安全的替代方案
通过理解这些核心概念和注意事项,开发者可以安全有效地使用Java序列化机制,同时避免潜在的安全风险。