当前位置: 首页 > news >正文

Java序列化、反序列化、反序列化漏洞

文章目录

    • 1 序列化和反序列化
      • 1.1 概念
      • 1.2 序列化可以做什么?
    • 3 实现方式
      • 3.1 Java 原生方式
      • 3.2 第三方方式
    • 4 反序列化漏洞

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;}@Overridepublic 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;}@Overridepublic 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 方法会负责根据序列化的数据正确地重建这些内部结构。


http://www.mrgr.cn/news/35730.html

相关文章:

  • 内衣洗衣机哪个牌子好用?五款业内口碑爆棚产品汇总
  • 低成本搭建企业专属云电脑 贝锐向日葵推出私有化云电脑服务
  • CentOS下安装Kibana(保姆级教程)
  • 14年408-计算机网络
  • Error: one input ui-file must be specified(问题已解决)
  • Unicode与ANSI字符串的转换(MultiByteToWideChar与WideCharToMultiByte)
  • AI绘画stable diffusion comfyui的api使用教程
  • luceda ipkiss教程 74:布尔运算去掉部分图层
  • Vue实战教程:手动封装一个全局可自定义图标提示组件
  • 【Elasticsearch】-实现图片向量相似检索
  • 4 php7.4中开发一个websocket 聊天相关配置调用
  • echarts横向柱状图让Y轴的名字和数量在柱状图上方展示
  • 四川财谷通信息技术有限公司抖音小店领域的新势力
  • 保姆级教程!0基础小白也适用,教你用AI美女在小红书“疯狂上分”
  • 企业源代码也需要加密?2024十款源代码加密软件排行榜
  • ChatWiki大模型介绍
  • 黑芝麻大算力SOC,A1000
  • .NET 一款执行命令回显的微软白名单工具
  • MySQL 8 临时关闭缓存
  • 【高等代数笔记】线性空间(十-十三)