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. 安全问题产生原因

当类满足以下条件时可能产生安全问题:

  1. 实现Serializable接口
  2. 重写readObject方法
  3. 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. 利用链三要素

  1. 入口类(Source)

    • 重写readObject方法
    • 实现Serializable接口
    • 调用常见方法
    • 参数类型宽泛
    • Java自带类(如HashMap
  2. 执行链(Gadget Chain)

    • 互相依赖的对象链
  3. 执行类(Sink)

    • 实际执行攻击的位置(如Runtime.exec()

4. HashMap作为入口类的分析

  • 实现了Serializable接口
  • 接受Object类型参数(K,V可以是任何类型)
  • 重写了readObject方法
  • readObject中调用了hashCode()方法

六、安全实践建议

  1. 避免反序列化不可信数据
  2. 使用白名单验证反序列化的类
  3. 考虑使用替代方案如JSON/XML序列化
  4. 对敏感字段使用transient关键字
  5. 更新Java环境,修复已知漏洞

七、扩展思考

  1. 研究Object#hashCode()Object#toString()Object#equals()方法的重写可能带来的安全问题
  2. 分析常见Java反序列化漏洞利用链(如Apache Commons Collections)
  3. 了解Java 9引入的过滤器机制(ObjectInputFilter
Java序列化与反序列化安全教学文档 一、Java序列化基础概念 1. 序列化与反序列化定义 序列化 :将Java对象转换为字节序列的过程,以便存储或传输 反序列化 :将字节序列恢复为Java对象的过程 2. 基本实现方式 要使一个类可序列化,必须实现 java.io.Serializable 接口: 二、序列化与反序列化代码实现 1. 序列化示例代码 2. 反序列化示例代码 三、transient关键字 用于标记不需要序列化的字段 被标记的字段在反序列化后会变为null 四、自定义序列化/反序列化过程 可以通过重写以下方法来自定义序列化行为: 五、反序列化安全问题 1. 安全问题产生原因 当类满足以下条件时可能产生安全问题: 实现 Serializable 接口 重写 readObject 方法 在 readObject 方法中执行危险操作 2. 恶意代码示例 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 )