Effective Java 第三版—— 85. 其他替代方式优于Java本身序列化

Tips
书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code
注意,书中的有些代码里方法是基于Java 9 API中的,所以JDK 最好下载 JDK 9以上的版本。

Effective Java 第三版—— 85. 其他替代方式优于Java本身序列化

序列化

本章涉及对象序列化(object serialization),它是Java的框架,用于将对象编码为字节流(序列化)并从其编码中重构对象(反序列化)。 一旦对象被序列化,其编码可以从一个虚拟机发送到另一个虚拟机或存储在磁盘上以便以后反序列化。 本章重点介绍序列化的风险以及如何将序列化的风险最小化。

  1. 其他替代方式优于Java本身序列化

当序列化在1997年添加到Java中时,它被认为有一定的风险。这种方法曾在研究语言(模块3)中尝试过,但从未在生产语言中使用过。虽然程序员不费什么力气就能实现分布式对象的承诺很吸引人,但代价是不可见的构造方法和API与实现之间模糊的界线,可能会出现正确性、性能、安全性和维护方面的问题。支持者认为收益大于风险,但历史证明并非如此。

本书前几版中描述的安全问题与一些人担心的一样严重。 2000年之前中讨论的漏洞在未来十年被转化为严重漏洞,其中最著名的包括2016年11月对旧金山大都会运输署(San Francisco Metropolitan Transit Agency)市政铁路(SFMTA Muni)的勒索软件攻击,导致整个收费系统关闭了两天[Gallagher16]。

序列化的一个基本问题是它的攻击面太大而无法保护,而且还在不断增长:通过调用 ObjectInputStream类上的 readObject方法反序列化对象图。这个方法本质上是一个神奇的构造方法,可以用来实例化类路径上几乎任何类型的对象,只要该类型实现Serializable接口。在反序列化字节流的过程中,此方法可以执行来自任何这些类型的代码,因此所有这些类型的代码都是攻击面的一部分。

攻击面包括Java平台类库中的类,第二方类库(如Apache Commons Collections)和应用程序本身。 即使你遵守所有相关的最佳实践并成功编写无法攻击的可序列化类,你的应用程序仍可能容易受到攻击。 引用CERT协调中心技术经理Robert Seacord的话:

Java反序列化是一个明显且存在的危险,因为它直接被应用程序广泛使用,并间接地由Java子系统(如RMI(远程方法调用),JMX(Java管理扩展)和JMS(Java消息系统))广泛使用。 不受信任的流的反序列化可能导致远程代码执行(RCE),拒绝服务(DoS)以及一系列其他漏洞利用。 应用程序即使没有做错也容易受到这些攻击。[Seacord17]

攻击者和安全研究人员研究Java类库和常用的第三方类库中的可序列化类型,寻找在反序列化过程中调用的执行潜在危险活动的方法。这种方法称为gadget。多个gadget可以同时使用,形成一个gadget链(chain)。偶尔会发现gadget链,它的功能足够强大,允许攻击者在底层硬件上执行任意的本机代码,只要有机会提交精心设计的字节流进行反序列化。这正是SFMTA Muni袭击中发生的事情。这次袭击并不是孤立事件。已经发生过,而且还会有更多。

不使用任何gadget,就可以通过导致需要很长时间反序列化的短字节流,进行反序列化操作,轻松地发起拒绝服务攻击。这种流被称为反序列化炸弹(deserialization bombs)[Svoboda16]。下面是Wouter Coekaerts的一个例子,它只使用HashSet和字符串[Coekaerts15]:

// Deserialization bomb - deserializing this stream takes forever
static byte[] bomb() {
    Set<object> root = new HashSet<>();
    Set<object> s1 = root;
    Set<object> s2 = new HashSet<>();
    for (int i = 0; i < 100; i++) {
        Set<object> t1 = new HashSet<>();
        Set<object> t2 = new HashSet<>();
        t1.add("foo"); // Make t1 unequal to t2
        s1.add(t1);  s1.add(t2);
        s2.add(t1);  s2.add(t2);
        s1 = t1;
        s2 = t2;
    }
    return serialize(root); // Method omitted for brevity
}
</object></object></object></object></object>

对象图由201个HashSet实例组成,每个实例包含3个或更少的对象引用。整个流的长度为5744字节,但是在完成反序列化之前,太阳都已经耗尽了。问题是反序列化HashSet实例需要计算其元素的哈希码。 root实例的2个元素本身就是包含2个HashSet元素的HashSet,每个HashSet元素包含2个HashSet元素,以此类推,深度为100。因此,反序列化set会导致hashCode方法被调用超过2100次。除了反序列化会持续很长时间之外,反序列化器没有任何错误的迹象。生成的对象很少,并且堆栈深度是有界的。

那么你能做些什么来抵御这些问题呢? 每当反序列化你不信任的字节流时,就会打开攻击。 避免序列化漏洞利用的最佳方法是永远不要反序列化任何东西。用1983年电影《战争游戏》(WarGames)中名为约书亚(Joshua)的电脑的话来说,”唯一的制胜的招式就是不玩”。 没有理由在你编写的任何新系统中使用Java序列化。 还有其他在对象和字节序列之间进行转换的机制,可以避免Java序列化的许多危险,同时提供许多优势,例如跨平台支持,高性能,大型工具生态系统以及广泛的专业知识社区。 在本书中,我们将这些机制称为跨平台结构化数据表示( cross-platform structured-data representations)。 虽然其他人有时将它们称为序列化系统,但本书避免了这种用法,以防止与Java序列化混淆。

这些表示的共同点是它们比Java序列化简单得多。它们不支持任意对象图的自动序列化和反序列化。相反,它们支持由一组属性值对(attribute-value pairs)组成的简单结构化数据对象。只支持少数基本数据类型和数组数据类型。事实证明,这个简单的抽象足以构建功能极其强大的分布式系统,而且足够简单,可以避免Java序列化从一开始就存在的严重问题。

领先的跨平台结构化数据表示是JSON [JSON]和Protocol Buffers,也称为protobuf [Protobuf]。 JSON由Douglas Crockford设计用于浏览器——服务器通信,并且Protocol Buffers由Google设计用于在其服务器之间存储和交换结构化数据。 即使这些表示有时被称为中立语言(language-neutral),JSON最初是为JavaScript开发的,而protobuf是为C++开发的; 这两种表述都保留了其起源的痕迹。

JSON和protobuf之间最显着的区别是JSON是基于文本的,人类可读的,而protobuf是二进制的,而且效率更高; JSON是一种专门的数据表示,而protobuf提供模式(类型)来文档记录和执行适当的用法。 尽管protobuf比JSON更有效,但JSON对于基于文本的表示非常有效。 虽然protobuf是二进制表示,但它确实提供了一种替代文本表示,用于需要人们可读性的地方(pbtxt)。

如果不能完全避免Java序列化,可能需要在它的遗留系统环境里中工作,那么下一个最佳选择就是 永远不要反序列化不受信任的数据。特别是,不应该接受来自不可信源的RMI流量。Java的官方安全编码指南说”反序列化不受信任的数据本质上是危险的,应该避免。这句话是用大号、粗体、斜体和红色字体设置的,它是整个文档中唯一应用这种处理([Java-secure)的文本。

如果无法避免序列化,并且不能绝对确定反序列化数据的安全性,那么可以使用Java 9中添加的对象反序列化过滤器,并将其移植到早期版本(Java .io. objectinputfilter)。该工具允许指定一个过滤器,该过滤器在反序列化之前应用于数据流。它在类的粒度上运行,允许接受或拒绝某些类。默认接受类并拒绝潜在危险类的列表称为黑名单;在默认情况下拒绝类并接受假定安全的类的列表称为白名单。 比起黑名单,更喜欢白名单,因为黑名单只保护你免受已知的威胁。一个名为Serial Whitelist Application Trainer (SWAT)的工具可用于应用程序自动准备一个白名单[Schneider16]。过滤工具还将保护免受过度使用内存和过深的对象图的影响,但它不能保护免受如上面所示的序列化炸弹的影响。

不幸的是,序列化在Java生态系统中仍然普遍存在。 如果要维护基于Java序列化的系统,请认真考虑迁移到跨平台的结构化数据表示,即使这可能是一项耗时的工作。 实际上,可能仍然发现自己必须编写或维护可序列化的类。 编写一个正确,安全,高效的可序列化类需要非常小心。 本章的其余部分提供了有关何时以及如何执行此操作的建议。

总之,序列化是危险的,应该避免。如果从头开始设计一个系统,可以使用跨平台的结构化数据表示,如JSON或protobuf。不要反序列化不受信任的数据。如果必须这样做,请使用对象反序列化过滤器,但要注意,它不能保证阻止所有攻击。避免编写可序列化的类。如果你必须这样做,一定要非常小心。

Original: https://www.cnblogs.com/IcanFixIt/p/10651006.html
Author: 林本托
Title: Effective Java 第三版—— 85. 其他替代方式优于Java本身序列化

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/537064/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • spring 事务回滚

    1、遇到的问题 当我们一个方法里面有多个数据库保存操作的时候,中间的数据库操作发生的错误。伪代码如下: public method() { Dao1.save(Person1); …

    Java 2023年5月30日
    082
  • 2022-8-10 JAVA的反射机制

    反射机制 AVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方…

    Java 2023年6月13日
    078
  • 瀑布流ListView

    前言 终于忙完了一段时间,现在前段时间写的一个瀑布流ListView到想法分享下,这个东西是扩展自Listview,当列表内容拉到最后后触发刷新操作,以便抓取更多到数据。 先贴下整…

    Java 2023年6月7日
    077
  • 多线程与高并发(三)—— 源码解析 AQS 原理

    一、前言 AQS 是一个同步框架,关于同步在操作系统(一)—— 进程同步 中对进程同步做了些概念性的介绍,我们了解到进程(线程同理,本文基于 JVM 讲解,故下文只称线程)同步的工…

    Java 2023年6月9日
    079
  • 全文本搜索神器

    作为一个比较懒惰的人,文件经常放的找不到位置,整理后,又会由于层次太多,一层层打开文件夹,特别麻烦,对于文件搜索的问题,以前推荐过工具 Listary,可以非常好的解决这个问题。 …

    Java 2023年5月30日
    089
  • day04_数组

    学习目标: 1. jvm内存图入门 2. 一维数组的使用 3. 二维数组的使用 4. 数组的内存结构 5. 数组中常见算法 6. 数组中常见的异常 java程序运行在jvm上,jv…

    Java 2023年6月8日
    0133
  • [学习笔记] Java重写和重载

    重写(Override) 重写是子类对允许访问的父类方法的方法体重新进行编写,返回值和形参不发生改变; 通过重写,子类可以根据需要定义特定于自身的行为,根据需要实现父类的方法; 方…

    Java 2023年6月5日
    084
  • “假学习”与“真学习”

    什么叫做”假学习”? **一、看书 **买一堆书,有空看看。看书,这是典型的假学习。看书看不懂还在看,就是假学习,欺骗自己,安慰自己正在学习而已。专业书都写…

    Java 2023年6月9日
    099
  • 我是怎么入行做风控的

    引言 常听到周围有人说”风控”这个词,只知道这是一个神秘的部门,对他们做的事却一知半解,只知道这个风控部门对公司非常重要,任何活动和信息都最好向风控部门报备…

    Java 2023年6月15日
    0104
  • springboot小结

    创建一个SpringBoot项目 创建项目注意点 然后选中自己需要的依赖 不过后期还可以自己导入不过比较麻烦 分析各种包 不过的文件夹需要自己建 图标写成这样放到public下面就…

    Java 2023年6月7日
    099
  • web 前端 基础HTML知识点

    B/S(Browser/Server):浏览器实现 优点: 规范、使用方便、本身实现成本低 容易升级、便于维护 缺点: 没有网络,无法使用 保存数据量有限,和服务器交互频率高、耗费…

    Java 2023年6月6日
    0119
  • Spring Cloud Alibaba分布式事务组件 seata 详解(小白都能看懂)

    一,什么是事务(本地事务)? 指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。 简单的说,事务就是并发控制的单位,是用户定义的一个操作序列。 _而一个逻辑…

    Java 2023年6月14日
    094
  • JAVA自已设计JSON解析器

    当然,有很多很好的JSON解析的JAR包,比如JSONOBJECT,GSON,甚至也有为我们测试人员而打造的JSONPATH,但我还是自已实现了一下(之前也实现过,现在属于重构)。…

    Java 2023年5月29日
    054
  • Java中如何数组进行反转呢?

    下文笔者将讲述java代码数组反转的方法分享,如下所示: 数组是我们日常开发中常用过的一种数据结构,那么我们如何将一个数组反转操作呢? 下文笔者借助栈对象的先进后出的特性, 首先将…

    Java 2023年6月15日
    077
  • Linux系统中Redis和Tomcat的PID文件路径设置

    Tomcat: /bin/catalina.sh 文件头注释下面添加一行:CATALINA_PID=/var/run/tomcat.pid Redis: redis.conf配置文…

    Java 2023年6月5日
    0109
  • 《Java编程思想》读书笔记(二)

    三年之前就买了《Java编程思想》这本书,但是到现在为止都还没有好好看过这本书,这次希望能够坚持通读完整本书并整理好自己的读书笔记,上一篇文章是记录的第一章到第十章的内容,这一次记…

    Java 2023年6月5日
    0132
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球