java序列化和反序列化基础

java序列化和反序列化基础

概述

Java序列化是指把Java对象转换为字节序列的过程(将对象在内存中的状态(字段的值等)转化为序列)

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

这样做可以将属性存放到数据库中

这么做原因:要在两台机器之间通过这些序列传输对象

常见的序列化和反序列化协议:

  • XML&SOAP
  • JSON(Javascript Object Notation)
  • Protobuf

序列化是针对对象的

实现

序列化:

1
2
3
4
5
6
public class SerializationTest {
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }
}

写一个类,将内容存入文件,调用writeObject方法将其序列化

反序列化:

1
2
3
4
5
6
7
public class UnserializeTest {
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
        ObjectInputStream ois= new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
}

将序列化内容存入文件,readObject读取实现反序列化

注意:

1
2
public class Person implements Serializable{
}

读取接口Serializable才可以序列化

输入输出流

注意点

  • ObjectOutputStream代表对象输出流:

    它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。

  • ObjectInputStream代表对象输入流:

    它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。

  • 一个实现 Serializable 接口的子类也是可以被序列化的。

  • 静态成员变量是不能被序列化,序列化是针对对象属性的,而静态成员变量是属于类的。

  • transient 标识的对象成员变量不参与序列化

    image-20251111200638153

    此处name的变量被transient标记

  • idea原生可以自己写readObject()和writeObject()

    比如在建立一个数组下限是100,但是实际使用了30,而全部序列化产生了浪费

    所以可以自己写方法,调用时,优先调用自己写的方法

  • serialVersionUID, 用于验证版本一致性

    发生改变有三种情况:

    1. 手动去修改导致当前的 serialVersionUID 与序列化前的不一样。
    2. 我们根本就没有手动去写这个 serialVersionUID 常量,那么 JVM 内部会根据类结构去计算得到这个 serialVersionUID 值,在类结构发生改变时(属性增加,删除或者类型修改了)这种也是会导致 serialVersionUID 发生变化。
    3. 假如类结构没有发生改变,并且没有定义 serialVersionUID ,但是反序列和序列化操作的虚拟机不一样也可能导致计算出来的 serialVersionUID 不一样。

漏洞危害

为什么会产生安全问题?:

只要服务端反序列化数据,客户端传递类的readObject中代码会自动执行,给予攻击者在服务器上运行代码的能力。

可能的形式:

  1. 入口类的readObject直接调用危险方法。

    自己写的readObject,直接加命令执行的危害代码

  2. 入口类参数中包含可控类,该类有危险方法,readObject时调用。(类套类)

    • 共同条件 继承Serializable

      • 入口类 source (重写readObject 参数类型宽泛 最好jdk自带)———>mapper HashMapper

        接口重写readObject原因:不同主机jvm,所写出来的哈希值不同,需要拆解对象,单独计算元素,需要重新指向readObject

      • 调用链 gadget chain (相同名称 相同类型)

      • 执行类 sink (rce ssrf 写文件等等)

  3. 入口类参数中包含可控类,该类又调用其他有危险方法的类,readObject时调用。


java序列化和反序列化基础
http://example.com/2025/11/14/java序列化和反序列化基础/
作者
Piggy Sprint
发布于
2025年11月14日
许可协议