重写的readObject()权限修饰符为什么不能是public
字数 1551 2025-08-22 22:47:30

Java反序列化中readObject()方法的权限修饰符探究

0x01 问题背景

在Java反序列化漏洞研究中,初学者常被告知:自定义序列化过程可以通过重写类的readObject方法实现。然而,一个关键细节引发了深入思考:为什么重写的readObject()方法必须是private修饰的?改为public后为何就不执行了?

0x02 方法重写的误解

首先需要澄清几个关键概念:

  1. 不是传统意义上的方法重写(Overriding)

    • 传统重写是子类覆盖父类的非final方法
    • Object类中并没有readObject()方法
    • Serializable只是一个标记接口,不包含任何方法
  2. 常见的错误认知

    • 误以为重写的是ObjectInputStreamreadObject()方法
    • 实际上ObjectInputStream.readObject()public final的,既不能重写也不是我们自定义类的父类方法

0x03 序列化机制深入解析

真正的机制隐藏在Java序列化API的实现细节中:

ObjectStreamClass的作用

ObjectStreamClass是Java序列化机制中的核心类,它负责:

  • 作为类的序列化描述符
  • 包装Class类,提取序列化相关信息
  • 包含serialVersionUID和需要序列化的字段信息

关键源码分析(基于OpenJDK):

// ObjectStreamClass.java
private Method getPrivateMethod(...) {
    // 查找指定类中的私有方法
    // ...
}

void initPrivateMethods() {
    // 查找readObject和writeObject方法
    readObjectMethod = getPrivateMethod(cl, "readObject",
        new Class<?>[] { ObjectInputStream.class }, Void.TYPE);
    // ...
}

序列化/反序列化流程

  1. 序列化时

    • ObjectOutputStream通过ObjectStreamClass查找目标类的private writeObject()方法
    • 如果存在,则调用自定义的序列化逻辑
  2. 反序列化时

    • ObjectInputStream通过ObjectStreamClass查找目标类的private readObject()方法
    • 如果存在,则调用自定义的反序列化逻辑
    • 不存在则使用默认的defaultReadFields方法

0x04 为什么必须是private

Java序列化机制设计如此的原因:

  1. 安全考虑

    • 防止外部代码直接调用这些敏感方法
    • 确保序列化/反序列化逻辑仅在序列化框架内部使用
  2. 设计意图

    • 这些方法是给序列化框架调用的,不是给应用程序直接调用的
    • 通过反射机制查找和调用,不依赖于传统的继承/重写机制
  3. 实现细节

    • ObjectStreamClassgetPrivateMethod()明确查找私有方法
    • 即使定义为public,序列化框架也不会调用

0x05 自定义序列化的正确方式

正确的自定义序列化方法示例:

public class Person implements Serializable {
    private String name;
    private int age;
    
    // 必须声明为private
    private void readObject(ObjectInputStream in) 
        throws IOException, ClassNotFoundException {
        // 自定义反序列化逻辑
        in.defaultReadObject(); // 调用默认反序列化
        // 额外的处理...
    }
    
    // 对应的writeObject也必须是private
    private void writeObject(ObjectOutputStream out) 
        throws IOException {
        // 自定义序列化逻辑
        out.defaultWriteObject(); // 调用默认序列化
        // 额外的处理...
    }
}

0x06 安全启示

  1. 反序列化漏洞利用

    • 攻击者常利用readObject()中的不安全操作
    • 即使方法为private,反序列化时仍会被调用
  2. 防御措施

    • 避免在readObject()中执行危险操作
    • 使用ObjectInputFilter限制反序列化的类
    • 考虑使用transient标记敏感字段

0x07 总结

  1. Java序列化中的"重写"readObject()实际上是声明一个特定签名的私有方法,而非传统意义上的方法重写
  2. 序列化框架通过反射查找并调用这些私有方法
  3. 方法必须声明为private,这是Java序列化机制的设计要求
  4. 理解这一机制对研究Java反序列化漏洞至关重要

0x08 进一步研究建议

  1. 阅读ObjectStreamClassObjectInputStream源码
  2. 研究Java序列化协议的实现细节
  3. 探索安全序列化替代方案(如JSON、Protocol Buffers等)
  4. 了解Java 9+引入的序列化过滤机制
Java反序列化中readObject()方法的权限修饰符探究 0x01 问题背景 在Java反序列化漏洞研究中,初学者常被告知: 自定义序列化过程可以通过重写类的 readObject 方法实现 。然而,一个关键细节引发了深入思考:为什么重写的 readObject() 方法必须是 private 修饰的?改为 public 后为何就不执行了? 0x02 方法重写的误解 首先需要澄清几个关键概念: 不是传统意义上的方法重写(Overriding) : 传统重写是子类覆盖父类的非final方法 Object 类中并没有 readObject() 方法 Serializable 只是一个标记接口,不包含任何方法 常见的错误认知 : 误以为重写的是 ObjectInputStream 的 readObject() 方法 实际上 ObjectInputStream.readObject() 是 public final 的,既不能重写也不是我们自定义类的父类方法 0x03 序列化机制深入解析 真正的机制隐藏在Java序列化API的实现细节中: ObjectStreamClass的作用 ObjectStreamClass 是Java序列化机制中的核心类,它负责: 作为类的序列化描述符 包装 Class 类,提取序列化相关信息 包含 serialVersionUID 和需要序列化的字段信息 关键源码分析(基于OpenJDK): 序列化/反序列化流程 序列化时 : ObjectOutputStream 通过 ObjectStreamClass 查找目标类的 private writeObject() 方法 如果存在,则调用自定义的序列化逻辑 反序列化时 : ObjectInputStream 通过 ObjectStreamClass 查找目标类的 private readObject() 方法 如果存在,则调用自定义的反序列化逻辑 不存在则使用默认的 defaultReadFields 方法 0x04 为什么必须是private Java序列化机制设计如此的原因: 安全考虑 : 防止外部代码直接调用这些敏感方法 确保序列化/反序列化逻辑仅在序列化框架内部使用 设计意图 : 这些方法是给序列化框架调用的,不是给应用程序直接调用的 通过反射机制查找和调用,不依赖于传统的继承/重写机制 实现细节 : ObjectStreamClass 的 getPrivateMethod() 明确查找私有方法 即使定义为 public ,序列化框架也不会调用 0x05 自定义序列化的正确方式 正确的自定义序列化方法示例: 0x06 安全启示 反序列化漏洞利用 : 攻击者常利用 readObject() 中的不安全操作 即使方法为 private ,反序列化时仍会被调用 防御措施 : 避免在 readObject() 中执行危险操作 使用 ObjectInputFilter 限制反序列化的类 考虑使用 transient 标记敏感字段 0x07 总结 Java序列化中的"重写" readObject() 实际上是 声明一个特定签名的私有方法 ,而非传统意义上的方法重写 序列化框架通过反射查找并调用这些私有方法 方法必须声明为 private ,这是Java序列化机制的设计要求 理解这一机制对研究Java反序列化漏洞至关重要 0x08 进一步研究建议 阅读 ObjectStreamClass 和 ObjectInputStream 源码 研究Java序列化协议的实现细节 探索安全序列化替代方案(如JSON、Protocol Buffers等) 了解Java 9+引入的序列化过滤机制