001、java序列化基础
字数 927 2025-08-29 22:41:38
Java序列化与反序列化安全教学文档
一、Java序列化基础概念
1. 序列化与反序列化定义
- 序列化:将Java对象转换为字节序列的过程,以便存储或传输
- 反序列化:将字节序列恢复为Java对象的过程
2. 基本实现方式
要使一个类可序列化,必须实现java.io.Serializable接口:
public class Person implements Serializable {
// 类内容
}
二、序列化与反序列化代码实现
1. 序列化示例代码
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Serializer {
public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream =
new ObjectOutputStream(new FileOutputStream("person.ser"));
objectOutputStream.writeObject(obj);
}
public static void main(String[] args) throws IOException {
Person person = new Person("test", "123");
System.out.println(person);
serialize(person);
}
}
2. 反序列化示例代码
import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class Deserial {
public static Object deserialize(String filename) throws Exception {
ObjectInputStream objectInputStream =
new ObjectInputStream(new FileInputStream(filename));
return objectInputStream.readObject();
}
public static void main(String[] args) throws Exception {
Object deserialized = deserialize("person.ser");
if (deserialized instanceof Person) {
System.out.println((Person)deserialized);
} else {
System.out.println(deserialized);
}
}
}
三、transient关键字
- 用于标记不需要序列化的字段
- 被标记的字段在反序列化后会变为null
public class Person implements Serializable {
transient String name; // 不会被序列化
String age; // 会被序列化
// 其他代码...
}
四、自定义序列化/反序列化过程
可以通过重写以下方法来自定义序列化行为:
private void writeObject(ObjectOutputStream oos) throws IOException {
// 自定义序列化逻辑
oos.defaultWriteObject(); // 调用默认序列化
}
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
// 自定义反序列化逻辑
ois.defaultReadObject(); // 调用默认反序列化
}
五、反序列化安全问题
1. 安全问题产生原因
当类满足以下条件时可能产生安全问题:
- 实现
Serializable接口 - 重写
readObject方法 - 在
readObject方法中执行危险操作
2. 恶意代码示例
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException, InterruptedException {
// 调用默认反序列化机制
ois.defaultReadObject();
// 恶意代码:执行计算器
Runtime runtime = Runtime.getRuntime();
Process calc = runtime.exec("calc");
calc.waitFor();
}
3. 利用链三要素
-
入口类(Source):
- 重写
readObject方法 - 实现
Serializable接口 - 调用常见方法
- 参数类型宽泛
- Java自带类(如
HashMap)
- 重写
-
执行链(Gadget Chain):
- 互相依赖的对象链
-
执行类(Sink):
- 实际执行攻击的位置(如
Runtime.exec())
- 实际执行攻击的位置(如
4. HashMap作为入口类的分析
- 实现了
Serializable接口 - 接受
Object类型参数(K,V可以是任何类型) - 重写了
readObject方法 - 在
readObject中调用了hashCode()方法
六、安全实践建议
- 避免反序列化不可信数据
- 使用白名单验证反序列化的类
- 考虑使用替代方案如JSON/XML序列化
- 对敏感字段使用
transient关键字 - 更新Java环境,修复已知漏洞
七、扩展思考
- 研究
Object#hashCode()、Object#toString()和Object#equals()方法的重写可能带来的安全问题 - 分析常见Java反序列化漏洞利用链(如Apache Commons Collections)
- 了解Java 9引入的过滤器机制(
ObjectInputFilter)