Java web学习之路-序列化和反序列化
字数 1128 2025-08-18 11:38:22

Java序列化与反序列化深度解析

序言

Java序列化与反序列化是Java安全中的重要概念,也是许多安全漏洞的根源。本文将全面解析Java序列化机制,包括基础概念、实现方式、安全风险以及实际利用方法。

一、序列化基础

1.1 序列化概念

Java序列化是指把Java对象转换为字节序列的过程,便于保存在内存、文件或数据库中。反序列化则是把字节序列恢复为Java对象的过程。

关键类与方法:

  • ObjectOutputStream.writeObject() - 序列化方法
  • ObjectInputStream.readObject() - 反序列化方法

1.2 序列化条件

一个类要可序列化必须满足:

  1. 实现java.io.Serializable接口
  2. 所有属性必须是可序列化的

1.3 基本示例

import java.io.Serializable;

public class Employee implements Serializable {
    public String name;
    public String identify;
    
    public void mailCheck() {
        System.out.println("This is the "+this.identify+" of our company");
    }
}

序列化操作:

FileOutputStream fileOut = new FileOutputStream("employee1.obj");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(e);
out.close();
fileOut.close();

二、序列化实现方式

2.1 Serializable接口

最常用的序列化方式,只需实现Serializable接口即可。

特性:

  • 只保存实例变量,不保存静态变量
  • 父类实现Serializable,子类自动可序列化
  • 会保存private变量,存在安全隐患
  • 不会序列化transient修饰的变量
  • 反序列化时不调用构造方法

2.2 transient关键字

阻止变量被序列化:

private transient String password;

2.3 Externalizable接口

继承自Serializable,增加了两个方法:

void writeExternal(ObjectOutput out) throws IOException;
void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;

特点:

  • 完全控制序列化过程
  • transient关键字无效
  • 必须提供无参构造函数

示例:

class Person implements Externalizable {
    transient String userName;  // 即使使用transient也会被序列化
    
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(userName);
        // 自定义序列化逻辑
    }
    
    @Override
    public void readExternal(ObjectInput in) throws IOException {
        userName = (String) in.readObject();
        // 自定义反序列化逻辑
    }
}

三、反序列化机制

3.1 基本反序列化

FileInputStream fileIn = new FileInputStream("employee1.obj");
ObjectInputStream in = new ObjectInputStream(fileIn);
Employee e = (Employee) in.readObject();
in.close();
fileIn.close();

3.2 readObject方法

反序列化的核心是readObject()方法,可以被重写以实现自定义行为:

private void readObject(java.io.ObjectInputStream in) 
    throws IOException, ClassNotFoundException {
    in.defaultReadObject();  // 默认反序列化
    // 自定义逻辑
}

四、安全风险与利用

4.1 反序列化漏洞原理

通过重写readObject()方法,可以在反序列化时执行任意代码:

class ObjectCalc implements Serializable {
    public String name;
    
    private void readObject(java.io.ObjectInputStream in) 
        throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        Runtime.getRuntime().exec("open /Applications/Calculator.app/");
    }
}

4.2 漏洞利用步骤

  1. 创建恶意对象并序列化:
ObjectCalc myObj = new ObjectCalc();
FileOutputStream fos = new FileOutputStream("object");
ObjectOutputStream os = new ObjectOutputStream(fos);
os.writeObject(myObj);
os.close();
  1. 受害者反序列化时触发:
FileInputStream fis = new FileInputStream("object");
ObjectInputStream ois = new ObjectInputStream(fis);
ObjectCalc objectFromDisk = (ObjectCalc)ois.readObject();
ois.close();

五、防御措施

  1. 避免反序列化不可信数据
  2. 使用ObjectInputFilter验证反序列化类
  3. 对敏感字段使用transient
  4. 重写readObject()添加验证逻辑
  5. 考虑使用JSON等替代序列化方式

六、关键总结

  1. Java序列化特征:aced0005开头
  2. 序列化保存实例变量,不保存静态变量
  3. transient阻止序列化,但对Externalizable无效
  4. 反序列化不调用构造方法
  5. readObject()重写是漏洞根源
  6. 反序列化顺序必须与序列化顺序一致

参考资源

  1. Java官方序列化文档
  2. Oracle安全编码指南
  3. Java反序列化漏洞详解

完整示例代码可在GitHub获取:Java序列化示例代码

Java序列化与反序列化深度解析 序言 Java序列化与反序列化是Java安全中的重要概念,也是许多安全漏洞的根源。本文将全面解析Java序列化机制,包括基础概念、实现方式、安全风险以及实际利用方法。 一、序列化基础 1.1 序列化概念 Java序列化是指把Java对象转换为字节序列的过程,便于保存在内存、文件或数据库中。反序列化则是把字节序列恢复为Java对象的过程。 关键类与方法: ObjectOutputStream.writeObject() - 序列化方法 ObjectInputStream.readObject() - 反序列化方法 1.2 序列化条件 一个类要可序列化必须满足: 实现 java.io.Serializable 接口 所有属性必须是可序列化的 1.3 基本示例 序列化操作: 二、序列化实现方式 2.1 Serializable接口 最常用的序列化方式,只需实现 Serializable 接口即可。 特性: 只保存实例变量,不保存静态变量 父类实现Serializable,子类自动可序列化 会保存private变量,存在安全隐患 不会序列化 transient 修饰的变量 反序列化时不调用构造方法 2.2 transient关键字 阻止变量被序列化: 2.3 Externalizable接口 继承自 Serializable ,增加了两个方法: 特点: 完全控制序列化过程 transient 关键字无效 必须提供无参构造函数 示例: 三、反序列化机制 3.1 基本反序列化 3.2 readObject方法 反序列化的核心是 readObject() 方法,可以被重写以实现自定义行为: 四、安全风险与利用 4.1 反序列化漏洞原理 通过重写 readObject() 方法,可以在反序列化时执行任意代码: 4.2 漏洞利用步骤 创建恶意对象并序列化: 受害者反序列化时触发: 五、防御措施 避免反序列化不可信数据 使用 ObjectInputFilter 验证反序列化类 对敏感字段使用 transient 重写 readObject() 添加验证逻辑 考虑使用JSON等替代序列化方式 六、关键总结 Java序列化特征: aced0005 开头 序列化保存实例变量,不保存静态变量 transient 阻止序列化,但对 Externalizable 无效 反序列化不调用构造方法 readObject() 重写是漏洞根源 反序列化顺序必须与序列化顺序一致 参考资源 Java官方序列化文档 Oracle安全编码指南 Java反序列化漏洞详解 完整示例代码可在GitHub获取: Java序列化示例代码