写点什么

Java 序列化的状态

  • 2018-09-10
  • 本文字数:6034 字

    阅读完需:约 20 分钟

关键要点

  • Java 序列化在很多库中引入了安全漏洞。
  • 对序列化进行模块化处于开放讨论状态。
  • 如果序列化能够成为模块,开发人员将能够将其从攻击表面上移除。
  • 移除其他模块可以消除它们所带来的风险。
  • 插桩提供了一种编织安全控制的方法,提供现代化的防御机制。

多年来,Java 的序列化功能饱受安全漏洞 zero-day 攻击,为此赢得了“持续奉献的礼物”和“第四个不可饶恕的诅咒”的绰号。

作为回应,OpenJDK 贡献者团队讨论了一些用于限制序列化访问的方法,例如将其提取到可以被移除的jigsaw 模块中,让黑客无法攻击那些不存在的东西。

一些文章(例如“序列化必须死”)提出了这样的建议,将有助于防止某些流行软件(如VCenter 6.5)的漏洞被利用

什么是序列化?

自从 1997 年发布 JDK 1.1 以来,序列化已经存在于 Java 平台中。

它用于在套接字之间共享对象表示,或者将对象及其状态保存起来以供将来使用(反序列化)。

在 JDK 10 及更低版本中,序列化作为 java.base 包和 java.io.Serializable 方法的一部分存在于所有的系统中。

GeeksForGeeks 对序列化的工作原理进行了详细的描述。

有关更多如何使用序列化的代码示例,可以参看 Baeldung 对 Java 序列化的介绍

序列化的挑战和局限

序列化的局限主要表现在以下两个方面:

  1. 出现了新的对象传输策略,例如 JSON、XML、Apache Avro、Protocol Buffers 等。
  2. 1997 年的序列化策略无法预见现代互联网服务的构建和攻击方式。

进行序列化漏洞攻击的基本前提是找到对反序列化的数据执行特权操作的类,然后传给它们恶意的代码。为了理解完整的攻击过程,可以参看 Matthias Kaiser 在 2015 年发表的“ Exploiting Deserialization Vulnerabilities in Java ”一文,其中幻灯片第 14 页开始提供了相关示例。

其他大部分与序列号有关的安全研究都是基于Chris Frohoff、Gabriel Lawrence 和Alvaro Munoz 的工作成果。

序列化在哪里?如何知道我的应用程序是否用到了序列化?

要移除序列化,需要从 java.io 包开始,这个包是 java.base 模块的一部分。最常见的使用场景是:

使用这些方法的开发人员应考虑使用其他存储和读回数据的替代方法。Eishay Smith 发布了几个不同序列化库的性能指标。在评估性能时,需要在基准度量指标中包含安全方面的考虑。默认的Java 序列化“更快”一些,但漏洞也会以同样的速度找上门来。

我们该如何降低序列化缺陷的影响?

项目Amber 包含了一个关于将序列化API 隔离出来的讨论。我们的想法是将序列化从java.base 移动到单独的模块,这样应用程序就可以完全移除它。在确定 JDK 11 功能集时并没有针对该提议得出任何结果,但可能会在未来的 Java 版本中继续进行讨论。

通过运行时保护来减少序列化暴露

一个可以监控风险并自动化可重复安全专业知识的系统对于很多企业来说都是很有用的。Java 应用程序可以将 JVMTI 工具嵌入到安全监控系统中,通过插桩的方式将传感器植入到应用程序中。Contrast Security 是这个领域的一个免费产品,它是 JavaOne 大会的 Duke's Choice 大奖得主。与其他软件项目(如 MySQL 或 GraalVM)类似, Contrast Security 的社区版对开发人员是免费的。

将运行时插桩应用在 Java 安全性上的好处是它不需要修改代码,并且可以直接集成到 JRE 中。

它有点类似于面向切面编程,将非侵入式字节码嵌入到源端(远程数据进入应用程序的入口)、接收端(以不安全的方式使用数据)和转移(安全跟踪需要从一个对象移动到另一个对象)。

通过集成每个“接收端”(如 ObjectInputStream),运行时保护机制可以添加额外的功能。在从 JDK 9 移植反序列化过滤器之前,这个功能对序列化和其他攻击的类型(如 SQL 注入)来说至关重要。

集成这个运行时保护机制只需要修改启动标志,将 javaagent 添加到启动选项中。例如,在 Tomcat 中,可以在 bin/setenv.sh 中添加这个标志:

CATALINA_OPTS=-javaagent:/Users/ecostlow/Downloads/Contrast/contrast.jar

启动后,Tomcat 将会初始化运行时保护机制,并将其注入到应用程序中。关注点的分离让应用程序可以专注在业务逻辑上,而安全分析器可以在正确的位置处理安全性。

其他有用的安全技术

在进行维护时,可以不需要手动列出一长串东西,而是使用像 OWASP Dependency-Check 这样的系统,它可以识别出已知安全漏洞的依赖关系,并提示进行升级。也可以考虑通过像 DependABot 这样的系统进行库的自动更新。

虽然用意很好,但默认的 Oracle 序列化过滤器存在与 SecurityManager 和相关沙箱漏洞相同的设计缺陷。因为需要混淆角色权限并要求提前了解不可知的事物,限制了这个功能的大规模采用:系统管理员不知道代码的内容,所以无法列出类文件,而开发人员不了解环境,甚至 DevOps 团队通常也不知道系统其他部分(如应用程序服务器)的需求。

移除未使用模块的安全隐患

Java 9 的模块化 JDK 能够创建自定义运行时镜像,移除不必要的模块,可以使用名为jlink 的工具将其移除。这种方法的好处是黑客无法攻击那些不存在的东西。

从提出模块化序列化到应用程序能够实际使用以及使用其他序列化的新功能需要一段时间,但正如一句谚语所说:“种树的最佳时间是二十年前,其次是现在”。

剥离 Java 的原生序列化功能还应该为大多数应用程序和微服务提供更好的互操作性。通过使用标准格式(如 JSON 或 XML),开发人员可以更轻松地在使用不同语言开发的服务之间进行通信——与 Java 7 的二进制 blob 相比,python 微服务通常具有更好的读取 JSON 文档的集成能力。不过,虽然 JSON 格式简化了对象共享,针对 Java 和.NET 解析器的“ Friday the 13th JSON attacks ”证明了银弹是不存在的(白皮书)。

在进行剥离之前,序列化让然保留在 java.base 中。这些技术可以降低与其他模块相关的风险,在序列化被模块化之后,仍然可以使用这些技术。

为 Apache Tomcat 8.5.31 模块化 JDK 10 的示例

在这个示例中,我们将使用模块化的 JRE 来运行 Apache Tomcat,并移除任何不需要的 JDK 模块。我们将得到一个自定义的 JRE,它具有更小的攻击表面,仍然能够用于运行应用程序。

确定需要用到哪些模块

第一步是检查应用程序实际使用的模块。OpenJDK 工具 jdeps 可以对 JAR 文件的字节码执行扫描,并列出这些模块。像大多数用户一样,对于那些不是自己编写的代码,我们根本就不知道它们需要哪些依赖项或模块。因此,我使用扫描器来检测并生成报告。

列出单个 JAR 文件所需模块的命令是:

jdeps -s JarFile.jar

它将列出模块信息:

tomcat-coyote.jar -> java.base
tomcat-coyote.jar -> java.management
tomcat-coyote.jar -> not found

最后,每个模块(右边的部分)都应该被加入到一个模块文件中,成为应用程序的基本模块。这个文件叫作 module-info.java,文件名带有连字符,表示不遵循标准的 Java 约定,需要进行特殊处理。

下面的命令组合将所有模块列在一个可用的文件中,在 Tomcat 根目录运行这组命令:

find . -name *.jar ! -path "./webapps/*" ! -path "./temp/*" -exec jdeps -s {} \; | sed -En "s/.* -\> (.*)/  requires \1;/p" | sort | uniq | grep -v "not found" | xargs -0 printf "module com.infoq.jdk.TomcatModuleExample{\n%s}\n"

这组命令的输出将被写入 lib/module-info.java 文件,如下所示:

module com.infoq.jdk.TomcatModuleExample{
  requires java.base;
  requires java.compiler;
  requires java.desktop;
  requires java.instrument;
  requires java.logging;
  requires java.management;
  requires java.naming;
  requires java.security.jgss;
  requires java.sql;
  requires java.xml.ws.annotation;
  requires java.xml.ws;
  requires java.xml;
}

这个列表比整个 Java 模块列表要短得多。

下一步是将这个文件放入 JAR 中:

javac lib/module-info.java
jar -cf lib/Tomcat.jar lib/module-info.class

最后,为应用程序创建一个 JRE:

jlink --module-path lib:$JAVA_HOME/jmods --add-modules ThanksInfoQ_Costlow --output dist

这个命令的输出是一个运行时,包含了运行应用程序所需的恰到好处的模块,没有任何性能开销,也没有了未使用模块中可能存在的安全风险。

与基础 JDK 10 相比,只用了 98 个核心模块中的 19 个。

java --list-modules

com.infoq.jdk.TomcatModuleExample
java.activation@10.0.1
java.base@10.0.1
java.compiler@10.0.1
java.datatransfer@10.0.1
java.desktop@10.0.1
java.instrument@10.0.1
java.logging@10.0.1
java.management@10.0.1
java.naming@10.0.1
java.prefs@10.0.1
java.security.jgss@10.0.1
java.security.sasl@10.0.1
java.sql@10.0.1
java.xml@10.0.1
java.xml.bind@10.0.1
java.xml.ws@10.0.1
java.xml.ws.annotation@10.0.1
jdk.httpserver@10.0.1
jdk.unsupported@10.0.1

运行这个命令后,就可以使用 dist 文件夹中的运行时来运行应用程序。

看看这个列表:部署插件(applet)消失了,JDBC(SQL)消失了,JavaFX 也不见了,很多其他模块也消失了。从性能角度来看,这些模块不再产生任何影响。从安全角度来看,黑客无法攻击那些不存在的东西。保留应用程序所需的模块非常重要,因为如果缺少这些模块,应用程序也无法正常运行。

关于作者

Erik Costlow 是甲骨文的 Java 8 和 9 产品经理,专注于安全性和性能。他的安全专业知识涉及威胁建模、代码分析和安全传感器增强。在进入技术领域之前,Erik 是一位马戏团演员,可以在三轮垂直独轮车上玩火。

查看英文原文 The State of Java Serialization

2018-09-10 18:222888
用户头像

发布了 731 篇内容, 共 459.4 次阅读, 收获喜欢 2004 次。

关注

评论

发布
暂无评论
发现更多内容

【11月】InfoQ写作平台荣耀月报:名单出炉!

InfoQ写作社区官方

话题讨论 热门活动

🏆【Alibaba中间件技术系列】「RocketMQ技术专题」让我们一起实践RocketMQ的服务搭建及配置操作

码界西柚

消息队列 Apache RocketMQ 12月日更

elk客户端与springboot整合

小鲍侃java

签约计划第二季

编曲时如何在FL Studio卷帘窗口修改单个音符音量

懒得勤快

Istio 实践手册 |服务网格框架对比

xcbeyond

istio 28天写作 12月日更 Istio 实践手册

数仓和数据中台长期霸权,数据湖最稳

用友BIP

数据胡

数据中台的OneID是个什么鬼

用友BIP

数据中台 主数据

小企业如何落地elk日志收集系统

小鲍侃java

内容合集 签约计划第二季

大型前端项目 DevOps 沉思录 —— CI 篇d

CODING DevOps

ci DevOps 持续集成 工作流

基于云的技术架构设计实践-第2篇

hackstoic

监控系统 签约计划第二季

自定义View:使用二阶贝塞尔曲线绘制正弦函数

Changing Lin

12月日更

在Linux系统发行版(以CentOS7为例)上部署ElasticSearch集群并启动Kibana和Logstash

为自己带盐

Centos 7 28天写作 Elastic Search 签约计划第二季 12月日更

在 ABAP 里模拟实现 Java Spring 的依赖注入

汪子熙

Java 28天写作 SAP abap 12月日更

在 Windows 笔记本上调试运行在 iOS 设备上的前端应用

汪子熙

大前端 web api iOS Developer 28天写作 12月日更

荣耀周榜公布!你上榜了么?2022.1.17-1.23

InfoQ写作社区官方

话题讨论 热门活动

VirtualBox 网络配置 NAT + Host-Only

李鑫磊

Kubernetes 虚拟机 网络配置 virtualbox

质量服务“一站式”平台搭建,NQI一站式云服务平台

电微13828808271

EasyRecovery如何恢复游戏——英雄联盟

淋雨

数据恢复 EasyRecovery

安全RCE之未授权访问分析

网络安全学海

网络安全 信息安全 渗透测试 WEB安全 漏洞挖掘

数据库自增 ID 用完了会咋样?

CRMEB

ONES 与极狐(GitLab)战略合作,强强联手推动 DevOps 发展

万事ONES

DevOps SaaS ONES #GitLab

在有道 | 同宇:一个正在老去的程序员

有道技术团队

程序员 技术人 工程师思维 人物访谈

logback获取bootstrap.yml配置

小鲍侃java

签约计划第二季

高可用 | repmgr 构建 PostgreSQL 高可用集群部署文档【建议收藏】

RadonDB

数据库 postgresql RadonDB

elk简介与背景

小鲍侃java

签约计划第二季

低代码发展正当时,中国将取代美国成为低代码开发的全球领导者

金小K

RPA 低代码 低代码开发 低代码平台

Linux一学就会之Centos-文件权限管理

学神来啦

云计算 Linux centos linux运维

一个基于HTML的可定制轮盘——帮你告别选择困难症

海拥(haiyong.site)

html 大前端 28天写作 签约计划第二季 12月日更

云编辑 | 使用 Cloud Studio 在线撰写预览并发布 Hugo 博客

宋天伦

CloudIDE Hugo Cloud Studio

实用机器学习笔记二:数据获取

打工人!

机器学习 学习笔记 12月日更 实用机器学习

面试腾讯:渗透测试工程师题型汇总

喀拉峻

面试 网络安全 安全 渗透测试

Java序列化的状态_Java_Erik Costlow_InfoQ精选文章