Java 中的序列化安全漏洞梳理(一)

背景
  • 现阶段公司会进行季度的安全巡检,扫描出来的 Java 相关漏洞,无论是远程代码执行、还是 JNDI 注入,基本都和 Java 的序列化机制有关。本文简单梳理了一下序列化机制相关知识,解释为什么这么多漏洞都和 Java 的序列化有关,以及后续怎么避免这些安全漏洞,减少版本升级工作量。同时能基于本文的知识,在看到序列化漏洞后,简单评估该漏洞对自身应用的影响

为什么需要序列化
  • 序列化主要是提供了一种机制,方便数据在网络之间进行传输,或者独立于程序存储在本地磁盘。

  • 序列化的使用场景很广,比如服务器收到请求参数,一种处理方式是一个个解析数据,自己构建处理数据。还有一种就是直接将数据反序列化为对象。当要把对象存储到缓存时,我们可以自己解析对象生成数据保存到缓存,取出时也自己处理数据转换为对象,也可以直接借助语言的序列化机制帮我们将对象序列化为数据存储起来,从缓存里获取数据后直接反序列化转为对象。在这些场景里,很明显序列化机制能极大改善我们的编程体验

Java 序列化基础知识简述
  • 最简单模式:implements Serializable

  • 我们可以只实现序列化接口,让 Java 序列化机制会帮我们处理其他的一切事情

  • 基于Serializable 接口简单定制

  • 可以用transient 指定不想序列化的数据,比如密码等敏感数据

  • 可以定义自己的writeObject()readObject() 方法,来自定义部分序列化和反序列化流程。注意这两个方法是私有的,却会被 Java 序列化机制自行调用

  • 基于Externalizable 接口全面定制

  • Externalizable接口提供了两个在序列化/反序列化时自动调用的方法:writeExternal()readExternal()

  • Serializable 对象的反序列化完全从其存储的字节位里构建,没有调用构造器。而Externalizable 对象反序列化时,会调用公共无参构造器,之后readExternal() 才调用。所以实现这个接口要提供无参构造器

  • Externalizable对象默认不存储自身的任何字段,因此transient关键字仅适用于Serializable对象

  • 序列化版本号serialVersionUID

  • 不管那种模式都自行定义版本号serialVersionUID

  • 如果我们不自行定义serialVersionUID,JVM 会根据自有的算法帮我们生成一个,这个算法是基于序列化类的字段、JVM 版本等等,这样可能导致即使类文件不变,升级 JDK 小版本都会导致反序列化失败

  • 另外,修改方法、transient 字段、静态字段都不影响版本号,这些都不是序列化内容,因此也不是序列化版本号生成的依据

  • JVM 生成的版本号可以通过jdk/bin/serialver命令获取,假设想知道某个类在某个 jvm 里的默认序列化版本号,可以用这个命令,来进行迁移改造

  • 注意,JSON、XML 等等,和 Java 内置序列化使用的字节流一样,都只是序列化数据的一种协议格式,其序列化机制还是基于 Java 的,并不是对 Java 序列化的一个替代。除了这类有构造和解析包就能直接使用的序列化方案,还有一类需要自己生成各种模板代码(Sub、Skeleton)的序列化方案,比如 rmi、Protocol Buffer 之类的,它们在 Java 侧的序列化一样是基于 Java 原生序列化的

Java 序列化的问题
  • JDK 的开发负责人说序列化是 Java 安全问题之源

Serialization was a horrible mistake in 1997, Some of us tried to fight it, but it went in, and there it is. ...We like to call serialization 'the gift that keeps on giving,' and the type of gift it keeps on giving is security vulnerab

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值