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 标识的对象成员变量不参与序列化

此处name的变量被transient标记
idea原生可以自己写readObject()和writeObject()
比如在建立一个数组下限是100,但是实际使用了30,而全部序列化产生了浪费
所以可以自己写方法,调用时,优先调用自己写的方法
serialVersionUID, 用于验证版本一致性
发生改变有三种情况:
- 手动去修改导致当前的 serialVersionUID 与序列化前的不一样。
- 我们根本就没有手动去写这个 serialVersionUID 常量,那么 JVM 内部会根据类结构去计算得到这个 serialVersionUID 值,在类结构发生改变时(属性增加,删除或者类型修改了)这种也是会导致 serialVersionUID 发生变化。
- 假如类结构没有发生改变,并且没有定义 serialVersionUID ,但是反序列和序列化操作的虚拟机不一样也可能导致计算出来的 serialVersionUID 不一样。
漏洞危害
为什么会产生安全问题?:
只要服务端反序列化数据,客户端传递类的readObject中代码会自动执行,给予攻击者在服务器上运行代码的能力。
可能的形式:
入口类的readObject直接调用危险方法。
自己写的readObject,直接加命令执行的危害代码
入口类参数中包含可控类,该类有危险方法,readObject时调用。(类套类)
共同条件 继承Serializable
入口类 source (重写readObject 参数类型宽泛 最好jdk自带)———>mapper HashMapper
接口重写readObject原因:不同主机jvm,所写出来的哈希值不同,需要拆解对象,单独计算元素,需要重新指向readObject
调用链 gadget chain (相同名称 相同类型)
执行类 sink (rce ssrf 写文件等等)
入口类参数中包含可控类,该类又调用其他有危险方法的类,readObject时调用。