java反序列化基础——(反)序列化、反射
字数 1263 2025-09-23 19:27:38
Java 反序列化基础:序列化、反序列化与反射
1. 序列化与反序列化基础概念
1.1 核心定义
Java序列化(Serialization):将Java对象转换为字节序列的过程
- 用途:对象状态保存到文件/数据库、网络传输对象、JVM间传递对象
反序列化(Deserialization):将字节序列重新构造成Java对象的逆过程
1.2 实现要求
- 必须实现
Serializable接口 - 序列化使用
ObjectOutputStream和writeObject()方法 - 反序列化使用
ObjectInputStream和readObject()方法
2. 序列化技术实现
2.1 基础序列化类定义
import java.io.Serializable;
public class User implements Serializable {
private static final long serialVersionUID = 8813939971390125541L;
private int age;
private String name;
public String nickName;
// Getter和Setter方法
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
@Override
public String toString() {
return "User{" + "age=" + age +
", name='" + name + '\'' +
", nickName='" + nickName + '\'' + '}';
}
}
2.2 序列化操作实现
import java.io.*;
public class SerializationExample {
public static void main(String[] args) throws IOException {
User user = new User();
user.setAge(99);
user.nickName = "嘿";
user.setName("Now");
// 方式1:文件序列化
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("user.ser"));
oos.writeObject(user);
oos.close();
// 方式2:字节流序列化(网络传输用)
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oosB = new ObjectOutputStream(baos);
oosB.writeObject(user);
byte[] byteArray = baos.toByteArray();
// 输出字节序列
for (byte b : byteArray) {
System.out.print(b + " ");
}
}
}
2.3 反序列化操作实现
import java.io.*;
public class DeserializationExample {
public static void main(String[] args)
throws IOException, ClassNotFoundException {
// 从文件反序列化
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("user.ser"));
Object o = ois.readObject();
System.out.println("文件反序列化: " + o.toString());
// 从字节数组反序列化
byte[] bytes = {-84,-19,0,5,115,114,0,4,85,115,101,114,122,81,103,-76,
-86,83,53,-27,2,0,3,73,0,3,97,103,101,76,0,4,110,97,109,
101,116,0,18,76,106,97,118,97,47,108,97,110,103,47,83,
116,114,105,110,103,59,76,0,8,110,105,99,107,78,97,109,
101,113,0,126,0,1,120,112,0,0,0,99,116,0,3,78,111,119,
116,0,3,-27,-104,-65};
ObjectInputStream oisByte = new ObjectInputStream(
new ByteArrayInputStream(bytes));
Object oByte = oisByte.readObject();
System.out.println("字节流反序列化: " + oByte.toString());
}
}
3. 自定义序列化与安全风险
3.1 重写readObject方法
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
public class User implements Serializable {
// ... 其他代码同上 ...
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject(); // 调用默认反序列化
System.out.println("User重写方法调用!");
// 这里可能执行危险操作!
}
}
3.2 安全风险说明
- 漏洞根源:自定义的
readObject()方法中执行了非预期操作 - 攻击向量:攻击者构造恶意序列化数据,在反序列化时执行危险代码
- 常见场景:执行系统命令、文件操作、网络连接等
4. Java反射机制
4.1 反射核心概念
反射允许程序在运行时检查、访问和修改类、方法、字段等元信息
4.2 获取Class对象的三种方式
// 方式1:通过类名.class
Class<?> clazz1 = User.class;
// 方式2:通过对象.getClass()
User user = new User();
Class<?> clazz2 = user.getClass();
// 方式3:通过Class.forName()
Class<?> clazz3 = Class.forName("com.example.User");
4.3 反射核心类(java.lang.reflect包)
Class:代表类和接口Field:提供类字段的信息和访问权限Method:提供类方法的信息和访问权限Constructor:提供类的构造方法信息
4.4 反射操作示例
import java.lang.reflect.*;
public class ReflectionExample {
public static void main(String[] args) throws Exception {
// 获取Class对象
Class<?> userClass = Class.forName("User");
// 创建实例
Object userInstance = userClass.newInstance();
// 获取并调用方法
Method setNameMethod = userClass.getMethod("setName", String.class);
setNameMethod.invoke(userInstance, "ReflectionTest");
// 访问字段(包括私有字段)
Field ageField = userClass.getDeclaredField("age");
ageField.setAccessible(true); // 突破私有限制
ageField.set(userInstance, 25);
// 调用toString查看结果
Method toStringMethod = userClass.getMethod("toString");
String result = (String) toStringMethod.invoke(userInstance);
System.out.println(result);
}
}
5. 反序列化漏洞模拟
5.1 漏洞形成机制
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject();
// 危险操作:执行系统命令
Runtime.getRuntime().exec("calc.exe");
}
5.2 攻击流程
- 攻击者构造恶意序列化数据
- 数据通过网络或文件传输到目标系统
- 目标系统执行反序列化操作
- 恶意代码在
readObject()方法中执行
5.3 防御措施
- 避免反序列化不可信数据
- 使用白名单验证反序列化对象
- 使用安全框架提供的安全反序列化方法
- 对
readObject()方法进行安全审计
6. 关键知识点总结
- 序列化必要条件:实现
Serializable接口 - 核心类:
ObjectOutputStream/ObjectInputStream - 核心方法:
writeObject()/readObject() - 安全风险:自定义
readObject()中的恶意代码执行 - 反射机制:运行时动态访问和操作类信息
- 攻击组合:反序列化 + 反射 = 远程代码执行
7. 实际应用注意事项
- 性能考虑:反射操作比直接调用慢,应谨慎使用
- 安全考虑:禁用不必要的序列化功能,验证输入数据
- 版本兼容:使用
serialVersionUID保持序列化版本一致性 - 敏感数据:使用
transient关键字保护敏感字段不被序列化
通过深入理解序列化、反序列化和反射机制,以及它们之间的相互作用,能够更好地理解和防御Java反序列化漏洞,提高应用程序的安全性。