java 序列化 对象状态
重要要点
- Java序列化在许多库中引入了安全漏洞。
- 开放讨论以模块化序列化。
- 如果序列化成为一个模块,则开发人员将能够从攻击面删除。
- 卸下其他模块可消除其风险。
- 仪器提供了一种编织安全控制的方法,可以提供当今的防御功能。
Java的序列化功能赢得了数年的安全利用和零日攻击 ,为其赢得了绰号,“ 不断给予的礼物 ”和“ 第四个不可原谅的诅咒 ”。
作为回应,OpenJDK贡献者团队讨论了限制访问序列化的方法,例如将其提取到可删除的竖锯模块中–黑客无法攻击不存在的内容。
这是根据流行文章(例如“ 序列化必死 ”)的建议采取的后续行动,将有助于防止诸如VCenter 6.5的流行软件受到利用 。
什么是序列化?
自1997年发布JDK 1.1以来,Java平台中就已经存在序列化。
它旨在作为一种轻量级的机制在套接字之间共享对象表示或存储对象及其状态以供将来检索(即反序列化)。
在JDK 10及更低版本中,序列化作为java.base和java.io.Serializable方法的一部分包含在所有系统中。
有关如何使用序列化的更多代码示例,Baeldung提供了Java序列化简介 。
序列化的挑战与局限
序列化受到两种主要方式的限制:
- 已经出现了更新的对象传输策略,例如JSON,XML, Apache Avro ,协议缓冲区等。
- 1997年的序列化策略无法预见现代互联网服务的构建和攻击方式。
序列化攻击的基本前提是寻找对反序列化的数据执行或执行特权操作的类,然后向它们传递带有恶意有效负载的该类的表示。 为了理解完整的演练,Matthias Kaiser在2015年发表的演讲“ 利用Java中的反序列化漏洞 ”提供了一个从幻灯片14开始的示例。
围绕序列化的许多安全性研究都基于 Chris Frohoff,Gabriel Lawrence和Alvaro Munoz的工作。
序列化在哪里,我怎么知道我的应用程序是否使用它?
删除序列化的更改来自java.io包,它是java.base模块的一部分。 最常见的用途是:
- 实现Serializable和(可选)
serialversionuid
长值字段。 - 使用
ObjectInputStream
或ObjectOutputStream
。 - 使用严重依赖序列化的库,例如:Xstream,Kryo,BlazeDS和大多数应用程序服务器 。
使用这些方法的开发人员应考虑改用其他方法来存储和读取其数据。 Eishay Smith发布了几种不同序列化库的性能指标 。 在评估性能时,需要将安全意识纳入基准指标。 在默认Java序列化为“更快”的情况下,漏洞利用程序以相同的速度运行。
今天我们如何限制序列化缺陷?
项目Amber中讨论了序列化API的隔离。 这个想法是序列化从java.base移到它自己的模块,应用程序可以将其完全删除。 在JDK 11功能集中 ,有关该建议的讨论并未取得成果,但可能会在将来的Java版本中进行研究。
现在通过运行时保护来限制序列化公开
许多组织将受益于可以监视风险,自动执行可重复的安全专业知识的系统。 Java应用程序可以通过利用JVMTI的工具将安全性监视嵌入到应用程序中,该工具使用仪器将传感器放置在应用程序内部。 该领域的一个免费产品是Contrast Security,它是JavaOne以前的Duke's Choice奖获得者 。 与其他软件项目(例如MySQL或GraalVM)类似, Contrast Security的社区版对开发人员免费 。
通过集成在每个“接收器”(例如序列化的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),开发人员可以更轻松地在以不同语言编写的服务之间进行通信-python微服务通常比Java 7 update X中的二进制blob具有更好的集成来读取JSON文档。格式简化了对象共享, 星期五对Java和.NET解析器的第13次JSON攻击表明没有灵丹妙药( 白皮书 )。
java.base
该切换之前,序列化将保留在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随附的每个JAR文件。 从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
该命令的输出是一个运行时,运行时仅具有运行应用程序所需的足够模块,而没有任何性能开销或未使用模块带来的安全风险。
要查看列表,请在标准JRE以及您刚在dist目录中创建的JRE上运行命令。 与基本的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文件夹中的运行时来运行应用程序。
查看此列表:部署插件(小应用程序)消失了,JDBC(SQL)消失了,JavaFX消失了,其他许多模块也消失了。 从性能角度来看,这些模块不再具有影响。 从安全角度看,黑客无法攻击不存在的内容。 准确性比修剪更重要。 保留应用程序所需的模块很重要,因为如果缺少模块,该应用程序也将无法运行。
java 序列化 对象状态