Java反序列化的基本概念
Java反序列化是指将字节流转换为Java对象的过程,与序列化(将对象转换为字节流)相反,序列化通常用于对象的持久化存储或网络传输,而反序列化则是将这些数据重新还原为可用的对象,在Java中,java.io.ObjectInputStream的readObject()方法是实现反序列化的核心,它读取输入流并重建对象,反序列化过程存在安全风险,如果处理不当,可能导致远程代码执行(RCE)等严重安全问题。

反序列化的实现机制
要实现反序列化,首先需要确保目标类实现了java.io.Serializable接口,该接口是一个标记接口,无需实现任何方法,序列化时,ObjectOutputStream将对象的状态写入字节流;反序列化时,ObjectInputStream通过readObject()方法解析字节流并重建对象。
// 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.ser"));
oos.writeObject(new MyObject());
oos.close();
// 反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.ser"));
MyObject obj = (MyObject) ois.readObject();
ois.close();
在上述代码中,readObject()方法会调用目标类的readObject()或readObjectNoData()方法(如果存在),以自定义反序列化逻辑,Java还提供了Externalizable接口,允许更精细地控制序列化和反序列化过程。
反序列化的安全风险
反序列化的主要风险在于攻击者可以构造恶意的字节流,通过readObject()方法执行任意代码。Commons Collections等库中的类在反序列化时会调用恶意代码,导致RCE,典型漏洞包括:

- 类加载攻击:攻击者通过字节流指定一个恶意类,当
readObject()尝试加载该类时触发代码执行。 - 方法调用攻击:利用反射或动态代理机制,在反序列化过程中调用危险方法(如
Runtime.exec())。 - 循环依赖或资源耗尽:构造复杂的字节流导致反序列化过程崩溃或消耗大量资源。
为缓解这些风险,Java引入了ObjectInputFilter机制,允许开发者过滤反序列化的类和对象。
ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(";!java.rmi.*;!com.sun.*");
ois.setObjectInputFilter(filter);
安全的实践建议
- 避免反序列化不可信数据:仅对来自可信来源的字节流进行反序列化。
- 使用白名单过滤:通过
ObjectInputFilter限制允许反序列化的类,避免加载危险类。 - 更新依赖库:及时修复第三方库中的反序列化漏洞(如Apache Commons Collections的补丁)。
- 替代方案:优先使用JSON、XML等文本格式进行数据传输,避免直接使用Java原生序列化。
反序列化的高级应用
尽管存在风险,反序列化在特定场景下仍有重要价值。
- 分布式系统:在RPC(远程过程调用)中,反序列化用于传输对象状态。
- 缓存机制:将序列化后的对象存储在缓存中,提高数据访问速度。
- 持久化框架:如Hibernate,通过序列化保存对象状态到数据库或文件。
在这些场景中,需结合安全措施确保反序列化的安全性,使用加密签名验证字节流的完整性,或采用自定义序列化逻辑替代默认实现。

Java反序列化是对象重建的重要机制,但伴随显著的安全风险,开发者需充分理解其原理,通过输入验证、类过滤和依赖库更新等手段降低风险,在不可信数据场景下,应优先选择更安全的序列化替代方案,随着Java版本的更新,反序列化的安全机制也在不断完善,例如Java 9引入的ObjectInputFilter和后续版本中的默认过滤规则,为开发者提供了更强大的防护工具,通过合理使用这些技术,可以在保证功能的同时,有效防范反序列化攻击。



















