java反序列化基础——(反)序列化、反射
字数 1263 2025-09-23 19:27:38

Java 反序列化基础:序列化、反序列化与反射

1. 序列化与反序列化基础概念

1.1 核心定义

Java序列化(Serialization):将Java对象转换为字节序列的过程

  • 用途:对象状态保存到文件/数据库、网络传输对象、JVM间传递对象

反序列化(Deserialization):将字节序列重新构造成Java对象的逆过程

1.2 实现要求

  • 必须实现 Serializable 接口
  • 序列化使用 ObjectOutputStreamwriteObject() 方法
  • 反序列化使用 ObjectInputStreamreadObject() 方法

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 攻击流程

  1. 攻击者构造恶意序列化数据
  2. 数据通过网络或文件传输到目标系统
  3. 目标系统执行反序列化操作
  4. 恶意代码在 readObject() 方法中执行

5.3 防御措施

  • 避免反序列化不可信数据
  • 使用白名单验证反序列化对象
  • 使用安全框架提供的安全反序列化方法
  • readObject() 方法进行安全审计

6. 关键知识点总结

  1. 序列化必要条件:实现 Serializable 接口
  2. 核心类ObjectOutputStream / ObjectInputStream
  3. 核心方法writeObject() / readObject()
  4. 安全风险:自定义 readObject() 中的恶意代码执行
  5. 反射机制:运行时动态访问和操作类信息
  6. 攻击组合:反序列化 + 反射 = 远程代码执行

7. 实际应用注意事项

  1. 性能考虑:反射操作比直接调用慢,应谨慎使用
  2. 安全考虑:禁用不必要的序列化功能,验证输入数据
  3. 版本兼容:使用 serialVersionUID 保持序列化版本一致性
  4. 敏感数据:使用 transient 关键字保护敏感字段不被序列化

通过深入理解序列化、反序列化和反射机制,以及它们之间的相互作用,能够更好地理解和防御Java反序列化漏洞,提高应用程序的安全性。

Java 反序列化基础:序列化、反序列化与反射 1. 序列化与反序列化基础概念 1.1 核心定义 Java序列化(Serialization) :将Java对象转换为字节序列的过程 用途:对象状态保存到文件/数据库、网络传输对象、JVM间传递对象 反序列化(Deserialization) :将字节序列重新构造成Java对象的逆过程 1.2 实现要求 必须实现 Serializable 接口 序列化使用 ObjectOutputStream 和 writeObject() 方法 反序列化使用 ObjectInputStream 和 readObject() 方法 2. 序列化技术实现 2.1 基础序列化类定义 2.2 序列化操作实现 2.3 反序列化操作实现 3. 自定义序列化与安全风险 3.1 重写readObject方法 3.2 安全风险说明 漏洞根源 :自定义的 readObject() 方法中执行了非预期操作 攻击向量 :攻击者构造恶意序列化数据,在反序列化时执行危险代码 常见场景 :执行系统命令、文件操作、网络连接等 4. Java反射机制 4.1 反射核心概念 反射允许程序在运行时检查、访问和修改类、方法、字段等元信息 4.2 获取Class对象的三种方式 4.3 反射核心类(java.lang.reflect包) Class :代表类和接口 Field :提供类字段的信息和访问权限 Method :提供类方法的信息和访问权限 Constructor :提供类的构造方法信息 4.4 反射操作示例 5. 反序列化漏洞模拟 5.1 漏洞形成机制 5.2 攻击流程 攻击者构造恶意序列化数据 数据通过网络或文件传输到目标系统 目标系统执行反序列化操作 恶意代码在 readObject() 方法中执行 5.3 防御措施 避免反序列化不可信数据 使用白名单验证反序列化对象 使用安全框架提供的安全反序列化方法 对 readObject() 方法进行安全审计 6. 关键知识点总结 序列化必要条件 :实现 Serializable 接口 核心类 : ObjectOutputStream / ObjectInputStream 核心方法 : writeObject() / readObject() 安全风险 :自定义 readObject() 中的恶意代码执行 反射机制 :运行时动态访问和操作类信息 攻击组合 :反序列化 + 反射 = 远程代码执行 7. 实际应用注意事项 性能考虑 :反射操作比直接调用慢,应谨慎使用 安全考虑 :禁用不必要的序列化功能,验证输入数据 版本兼容 :使用 serialVersionUID 保持序列化版本一致性 敏感数据 :使用 transient 关键字保护敏感字段不被序列化 通过深入理解序列化、反序列化和反射机制,以及它们之间的相互作用,能够更好地理解和防御Java反序列化漏洞,提高应用程序的安全性。