1 Java安全之反序列化( 二 )

:所有字节输入流的顶级父类 。

  • 接口 ObjectInput:ObjectInput 扩展了 DataInput 接口,DataInput 接口提供了从二进制流读取字节并将其重新转换为 Java 基础类型的功能,ObjectInput 额外提供了 readObject 方法用来读取类 。
  • 接口 ObjectStreamConstants:同上 。
  • ObjectInputStream 实现了反序列化功能,看一下其中的关键方法 。
    readObject从 ObjectInputStream 读取一个对象,将会读取对象的类、类的签名、类的非 transient 和非 static 字段的值,以及其所有父类类型 。
    我们可以使用 writeObjectreadObject 方法为一个类重写默认的反序列化执行方,所以其中 readObject 方法会 “传递性” 的执行,也就是说,在反序列化过程中,会调用反序列化类的 readObject 方法,以完整的重新生成这个类的对象 。
    readUnshared从 ObjectInputStream 读取一个非共享对象 。此方法与 readObject 类似,不同点在于readUnshared 不允许后续的 readObjectreadUnshared 调用引用这次调用反序列化得到的对象 。
    readObject0readObjectreadUnshared 实际上调用 readObject0 方法,readObject0是上面两个方法的基础实现 。
    readObjectOverride由 ObjectInputStream 子类调用,与 writeObjectOverride 一致 。
    通过上面对 ObjectOutputStream 和 ObjectInputStream 的了解,两个类的实现几乎是一种对称的、双生的方式进行
    反序列化漏洞一个类想要实现序列化和反序列化,必须要实现 java.io.Serializablejava.io.Externalizable 接口 。
    Serializable 接口是一个标记接口,标记了这个类可以被序列化和反序列化,而 Externalizable 接口在 Serializable 接口基础上,又提供了 writeExternalreadExternal 方法,用来序列化和反序列化一些外部元素 。
    其中,如果被序列化的类重写了 writeObject 和 readObject 方法,Java 将会委托使用这两个方法来进行序列化和反序列化的操作 。
    正是因为这个特性,导致反序列化漏洞的出现:在反序列化一个类时,如果其重写了 readObject 方法,程序将会调用它,如果这个方法中存在一些恶意的调用,则会对应用程序造成危害 。
    在这里我们利用写一个简单的测试程序,如下代码创建了 Person 类,实现了 Serializable 接口,并重写了 readObject 方法,在方法中使用 Runtime 执行命令弹出计算器
    public class Person implements Serializable {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {Runtime.getRuntime().exec("calc.exe");}}然后我们将这个类序列化并写在文件中,随后对其进行反序列化,就触发了命令执行
    public class SerializableTest {public static void main(String[] args) throws IOException, ClassNotFoundException {Person person = new Person("gk0d", 24);ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.txt"));oos.writeObject(person);oos.close();FileInputStream fis = new FileInputStream("test.txt");ObjectInputStream ois = new ObjectInputStream(fis);ois.readObject();ois.close();}}
    1 Java安全之反序列化

    文章插图
    那为什么我们重写了readObject就会执行呢?来看一下 java.io.ObjectInputStream#readObject() 方法的具体实现代码 。
    readObject 方法实际调用 readObject0 方法反序列化字符串
    1 Java安全之反序列化

    经验总结扩展阅读