JavaSec 基础之反序列化

时间:2024-03-11 10:19:55

文章目录

    • 序列化与反序列化
      • 一.什么是序列化与反序列化?
      • 二. 序列化与反序列化的原理
      • 三.应用场景
      • 四.原生序列化与反序列化的实现
        • 序列化
        • 反序列化
      • 五.漏洞成因

序列化与反序列化

一.什么是序列化与反序列化?

Java 序列化是指把 Java 对象转换为字节序列的过程;
Java 反序列化是指把字节序列恢复为 Java 对象的过程;

二. 序列化与反序列化的原理

Java序列化机制是基于对象的类结构进行的。当一个对象需要被序列化时,Java会将其转换为字节流,包括对象的数据和类的信息。这个字节流可以存储到文件中、传输到网络上,或者在分布式系统中传递给其他节点。

反序列化是将字节流转换回对象的过程。在反序列化过程中,Java会使用字节流中的信息重构对象,并将其重新加载到内存中。

Java的序列化机制是通过实现java.io.Serializable接口来实现的。该接口是一个标记接口,没有任何方法定义。只有实现了Serializable接口的类的对象才能被序列化。

三.应用场景

其一,实现了数据的持久化,通过序列化可以把数据永久的保存在硬盘上;其二,利用序列化实现远程通信,即在网络上传递对象的字节序列。

四.原生序列化与反序列化的实现

使用到JDK中关键类 **ObjectOutputStream**(对象输出流)**ObjectInputStream**(对象输入流)

ObjectOutputStream 类中:通过使用 writeObject(Object object) 方法,将对象以二进制格式进行写入。

ObjectInputStream 类中:通过使用 readObject()方法,从输入流中读取二进制流,转换成对象。
序列化

要将对象序列化,可以按照以下步骤进行操作:

  • 实现Serializable接口:将要序列化的类实现Serializable接口。
  • 创建ObjectOutputStream:创建一个ObjectOutputStream对象,用于将对象序列化为字节流。
  • 写入对象:使用writeObject()方法将对象写入到输出流中。
  • 关闭流:关闭输出流。
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class Person implements Serializable {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

public class SerializationExample {
    public static void main(String[] args) {
        Person person = new Person("John Doe", 30);

        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
            oos.writeObject(person);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

查看 person.ser 一堆乱码

image-20240308145149972

反序列化
  • 要将字节流反序列化为对象,可以按照以下步骤进行操作:
  • 创建ObjectInputStream:创建一个ObjectInputStream对象,用于从字节流中读取对象。
  • 读取对象:使用readObject()方法从输入流中读取对象。
  • 关闭流:关闭输入流。
import java.io.FileInputStream;
import java.io.IOException

        ;
import java.io.ObjectInputStream;

public class DeserializationExample {
    public static void main(String[] args) {
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {
            Person person = (Person) ois.readObject();
            System.out.println("Name: " + person.getName());
            System.out.println("Age: " + person.getAge());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

image-20240308132627764

五.漏洞成因

官方允许用户在被序列化的类中重写 readObject() 方法 , 重写后的方法将负责在反序列化时重构当前类对象 . 用户只需要在重写的 readObject() 方法中实现 defaultReadObject() 方法 , 就可以确保反序列化过程正常执行 .

序列化前,在 person 类中加入重写的 readObject 方法

private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        Runtime.getRuntime().exec("calc");
    }

那么反序列化调用的就是用户重写的 readObject。