1 序列化和反序列化

1.1 概念

Java 中序列化的意思是将运行时的对象转成可网络传输或者存储的字节流的过程。而反序列化正相反,是把字节流恢复成对象的过程。

1.2 序列化可以做什么?

  1. 持久化存储:将对象状态保存到存储设备(如硬盘)中,以便于后续读取使用。
  2. 网络传输:将对象转换成字节流,通过网络发送给另一个 JVM 实例,接收方再将字节流转回对象。
  3. 深度复制:通过序列化与反序列化可以实现对象的深复制,即创建一个新的对象,并且新对象的数据与原对象相同,但是它们在内存中的地址不同。

3 实现方式

3.1 Java 原生方式

step1:实现 Serializable 接口

要使一个类的对象能够被序列化,只需要让这个类实现 Serializable 接口即可。Serializable 是一个标记接口,它没有定义任何方法。例如:

public class Person implements Serializable {
// 可选,用于版本控制
private static final long serialVersionUID = 1L;
private String name;
private int age; public Person(String name, int age) {
this.name = name;
this.age = age;
} @Override
public String toString() {
return "Person{" +
"name='" + name + ''' +
", age=" + age +
'}';
} }

step2:使用 ObjectOutputStream#writeObject() 方法序列化。例如:

public static void main(String[] args) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\test.bin"));
oos.writeObject(new Person("zhangsan",18));
}

step3:使用 ObjectInputStream#readObject() 方法反序列化。例如:

public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(Paths.get("D:\test.bin")));
Person p = (Person) ois.readObject();
System.out.println(p);
}
// 打印 Person{name='zhangsan', age=18}

3.2 第三方方式

使用 Java 原生序列化方式序列化的对象只能被 Java 读取(反序列化),所以可以考虑先把对象转成一种通用的格式——如 JSON 字符串,然后把 JSON 字符串转成字节流进行网络传输,从而实现跨平台或者跨语言。

这个时候就可以使用市面上开源的序列化工具了,比如 JSON、Xml、hessian等。

4 反序列化漏洞

反序列化是把数据流转成对象,那么万一数据流被人恶意加工过呢?

拿 Java 原生的反序列化举例,反序列化需要调用 ObjectInputStream#readObject() 方法,但是如果数据流的对象自己重写了 readObject(),那 Java 便会调用自己的这个 readObject() 方法,这就给了攻击者可乘之机,他们就能在自己的 readObject() 方法里写攻击代码。

我们改造一下上面例子里的 Person 类:

@Data
public class Person implements Serializable {
// 可选,用于版本控制
private static final long serialVersionUID = 1L;
private String name;
private int age; public Person(String name, int age) {
this.name = name;
this.age = age;
} @Override
public String toString() {
return "Person{" +
"name='" + name + ''' +
", age=" + age +
'}';
} // 重写了 readObject 方法,反序列化时便会调用此处
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject(); //这一步是先让反序列化读取的时候按照默认的方法执行
Runtime.getRuntime().exec("calc"); //这一步是攻击代码,作用是打开windows系统计算器
}
}

此时再去执行反序列化便会打开系统的计算器,如果把打开计算器改成别的攻击代码,攻击者便能实现对系统的攻击。

为什么 Java 会允许我们重写 readObject,并让服务端调用我们的 readObject 呢?其实这么做的原因是为了方便定制化某些类的序列化方法,比如 HashMap 类就重写了 readObject 方法,原因是由于 HashMap 内部使用了一些特定的数据结构(如数组和链表/红黑树),直接反序列化可能无法正确地恢复这些内部结构。因此,readObject 方法会负责根据序列化的数据正确地重建这些内部结构。

Java序列化、反序列化、反序列化漏洞的更多相关文章

  1. java 序列化和反序列化的底层实现原理

    出处:序列化和反序列化的底层实现原理是什么? 一.基本概念1.什么是序列化和反序列化 (1)Java序列化是指把Java对象转换为字节序列的过程,而Java反序列化是指把字节序列恢复为Java对象的过 ...

  2. Java序列化与反序列化

    Java序列化与反序列化是什么?为什么需要序列化与反序列化?如何实现Java序列化与反序列化?本文围绕这些问题进行了探讨. 1.Java序列化与反序列化 Java序列化是指把Java对象转换为字节序列 ...

  3. [转] Java序列化与反序列化

    原文地址:http://blog.csdn.net/wangloveall/article/details/7992448 Java序列化与反序列化是什么?为什么需要序列化与反序列化?如何实现Java ...

  4. Java序列化与反序列化(Serializable)

    Java序列化与反序列化(Serializable) 特别注意: 1.要序列化的类必须实现Serializable借口 2.在反序列化(读取对象)的时候必须额外捕获EOFException 3.序列化 ...

  5. Java基础(五)-Java序列化与反序列化

    .output_wrapper pre code { font-family: Consolas, Inconsolata, Courier, monospace; display: block !i ...

  6. JAVA序列化和反序列化XML

    package com.lss.utils; import java.beans.XMLDecoder; import java.beans.XMLEncoder; import java.io.Bu ...

  7. Java序列化与反序列化(实践)

    Java序列化与反序列化(实践) 基本概念:序列化是将对象状态转换为可保持或传输的格式的过程.与序列化相对的是反序列化,它将流转换为对象.这两个过程结合起来,可以轻松地存储和传输数据. 昨天在一本书上 ...

  8. java序列化与反序列化(转)

    Java序列化与反序列化是什么?为什么需要序列化与反序列化?如何实现Java序列化与反序列化?本文围绕这些问题进行了探讨. 1.Java序列化与反序列化 Java序列化是指把Java对象转换为字节序列 ...

  9. Java序列化与反序列化是什么?为什么需要序列化与反序列化?如何实现Java序列化与反序列化?

    Java序列化与反序列化是什么?为什么需要序列化与反序列化?如何实现Java序列化与反序列化?本文围绕这些问题进行了探讨.  1.Java序列化与反序列化  Java序列化是指把Java对象转换为字节 ...

  10. (记录)Jedis存放对象和读取对象--Java序列化与反序列化

    一.理论分析 在学习Redis中的Jedis这一部分的时候,要使用到Protostuff(Protobuf的Java客户端)这一序列化工具.一开始看到序列化这些字眼的时候,感觉到一头雾水.于是,参考了 ...

随机推荐

  1. redis如何实现主从同步

    redis实现主从同步分为两种:全量同步和增量同步:第一次连入集群的slave需要进行全量同步,那些断开后重连的slave需要进行增量同步 每个redis都有自己的replid,他们是master的标 ...

  2. find命令在根目录查找文件

    find命令在根目录查找文件 find命令口诀是: find   路   名   含 一,首先看看路径的表示方法 .  表示当前目录 .. 表示上一级目录 cd ..  表示返回上一级目录cd ../ ...

  3. iOS开发基础144-逐字打印效果

    在AIGC类的APP中,实现那种一个字一个字.一行一行地打印出文字的效果,可以通过多种方法来实现.下面是一些实现方法,使用Swift和OC来举例说明. OC版 1. 基于定时器的逐字打印效果 可以使用 ...

  4. 【Java】PDF模板生成PDF文档

    一.需求背景 客户要求一份文书,文书内容有一些表单项,例如: 1.基本的是和否 (单选框或复选框) 2.备注内容(纯文本信息) 3.单位,机构组织,人员,字典项(下拉选择) 4.用户数字签名(图片信息 ...

  5. 视频推荐: Linux 的make自动化编译和通用makefile

    1.Linux 的make自动化编译原理 2.makefile编写规则 3.通用makefile的编写 ================================================ ...

  6. Ubuntu下手动设置Nvidia显卡风扇转速

    有显示器(桌面版) 默认情况下是可以调节的,神奇的是如果使用下面给出的命令调节的操作后就不能再进行可视化的手动调节了. ======================================== ...

  7. ELT已死,EtLT才是现代数据处理架构的终点!

    提到数据处理,经常有人把它简称为"ETL".但仔细说来,数据处理经历了ETL.ELT.XX ETL(例如,Reverse ETL.Zero-ETL)到现在流行的EtLT架构几次更迭 ...

  8. CH03_运算符

    CH03_运算符 算术运算符 作用:用于处理四则运算 示例: #include <iostream> using namespace std; int main() { int a = 1 ...

  9. ZPL Viewer工具网站

    新上线的ZPL Viewer工具网站 大家好! 在工作中,我们经常需要设计和预览ZPL(Zebra Programming Language)文件.以前,我一直使用ZPL Design这类工具,但后来 ...

  10. Linux input 子系统详解

    1. 模块概述 1.1.相关资料和代码研究 drivers/input/ include/uapi/linux/input-event-codes.h 2. 模块功能 linux核心的输入框架 3. ...