Java 作为顶级编程语言之一,已经在企业级软件开发领域活跃 25 年了。有人不断宣传 Java 已死,同时也有人坚称 Java 活得好好的。最近,又有报道称 log4j2 漏洞将再次“杀死” Java。我们曾开玩笑说,Java博物馆就好像是一个墓园,记录了每一次“死亡”的经过。
作者:Erik Costlow 译者:弯月
链接:https://foojay.io/today/log4j-isnt-killing-java/
上周,技术圈被 log4j2 漏洞掀起巨浪,各大安全公司纷纷发文介绍该漏洞的危害,并给出了各种临时解决方案。还有一些博主也发表文章教我们如何找到易受攻击的地方,并采取相应的防御措施。还有大量帖子跟着起哄,讨论如何采用一些不必要的防御技术。
目前,log4j2 官方团队已发布 2.16.0 新版本,加固漏洞防御机制。log4j2 是一款基于 Java 研发的开源日志系统,因此,当史诗级漏洞被爆出后,便有人宣称 log4j2 漏洞将再次“杀死” Java,已经被“死亡”过无数次的 Java,就像是开了一个墓园,记录每次“死亡”经过。
本文将简单介绍一下 Java 生态系统,说明什么是日志记录框架、在何处使用以及使用的原因,此外,还将介绍各个团队应当如何观察和控制 JVM 的行为。
Java 开发人员应该做哪些安全工作
快速给 JDK 和库打补丁是当前最有效的技术,可避免绝大多数大规模的黑客攻击。
给库文件打补丁(必需)
当代码库中存在漏洞时,最有效的技术就是打补丁以去除漏洞。如果不给库文件打补丁,那么应用程序很有可能被黑客入侵,攻击者将获得系统及其数据的完整访问权限。
无论在何种情况下,通常打补丁都很有效。
日志框架可能来自任何依赖项,可能由另一个库引入(即传递依赖项),而非由开发人员添加(即直接依赖项)。我们可以使用依赖项分析工具,例如 Contrast Community Edition,来检测依赖项和其他自定义的漏洞。
此外,还有一些分析依赖关系的开源工具,比如 Maven 依赖树(dependency:tree)和 Gradle 依赖树。NetBeans 等 IDE 也提供了依赖关系图可视化工具。
对于 log4j2 漏洞,你必须升级到 2.15.0 或更高版本。
给 JRE 打补丁,升级到 Java 的安全基线(推荐,定期执行)
每个Java 主要版本都会维护一个安全基线。由于每个季度 JDK 都会提供带有新安全改进的补丁,因此这个安全基线也会不断向前移动。低于安全基线的 Java 包含已知的安全问题,应该升级。
这是标准的安全最佳实践,与 log4j2 漏洞没有直接关系,也不会修复该漏洞。
各个团队可以使用 Foojay Disco API 自动监控安全基线,并及时地升级系统。开发人员可以将这个更新与 GitHub 操作相结合,确保在每次构建代码时,都使用最新的安全更新。如果发生安全事件,则立即升级 JRE,同时重新构建并重新部署代码。下面这个测试矩阵中就包含了这类的 GitHub 操作(https://github.com/foojayio/discoTestingMatrix)。
Java 安全基线的升级时间为:每年 1 月、4 月、7 月和 10 月,17 日前后的周二。详细信息包含在Oracle 重要补丁更新计划(https://www.oracle.com/security-alerts/)中,OpenJDK 漏洞组(https://foojay.io/pedia/security-vulnerability-management/)也采用了同一个计划。此外,如果出现重大问题,Oracle 还会提供计划外的安全更新。但 log4j2 漏洞不属于这种情况。
以下配置演示了如何使用 Java 11 的安全基线:
jobs:
java11:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
update: [x]
package: [jdk, jre]
fail-fast: false
max-parallel: 4
name: ${{ matrix.package }} 11.0.${{ matrix.update }}, ${{ matrix.os }}
steps:
- uses: actions/checkout@v1
- name: Set up JDK 11 Zulu
uses: foojayio/setup-java@disco
with:
java-package: ${{ matrix.package }}
java-version: 11.0.${{ matrix.update }}
distro: zulu
- name: java -version
run: java -version
定期检测自定义安全漏洞(推荐测试采用)
自动化安全工具可以捕获安全漏洞,不需要安全专业知识。将这类安全工具集成到 Java 应用程序,就可以实现安全监测,并记录安全信息。有些工具会根据依赖项的数量来决定是否存在漏洞,而这种方法则会根据依赖信息,由集成的分析器来报告这些依赖库的组合情况,并判断组合后是否安全。
例如,集成分析器不只是简单地检查 log4j2 存在与否及其版本,而且还可以确定攻击者是否可以控制远程日志输入。
此外,Contrast Community Edition 之类的免费分析器还能够即时捕获 log4j2,并捕获许多其他的安全漏洞,例如:
-
应用程序的 Hibernate、JBDC 或任何其他地方是否包含 SQL 注入问题?
-
远程用户能否控制发送到 Runtime.exec 的任何输入,即是否存在命令注入漏洞?
-
应用程序使用了哪些加密算法,在何处使用,是否符合适当的标准?
-
开发人员在结合使用多个库时,是否意外引入了某个安全漏洞,例如 OGNL 输入解析?
-
以及其他应用程序特有的安全漏洞。
使用 JDK Flight Recorder 监控安全事件
JDK Flight Recorder 是现代 OpenJDK 发行版中包含的性能分析工具,它不仅可以生成一些安全信息,而且开销非常低。各个团队可以使用 JDK Flight Recorder 来记录许多 IO 操作,例如 JRE 访问了哪些文件,或者哪些类会被反序列化。
通过使用 JDK Flight Recorder 监视 Java 应用程序事件,并通过数据流将事件传输到安全信息和事件管理(Security Information and Event Management,即SIEM)系统中,Java 团队就可以监视异常行为,并通过可防止漏洞的 Java 反序列化过滤器来检查各个类是否安全。
哪些安全措施没有太大作用
对于 log4j2 漏洞的问题,Web Application Firewall(WAF)之类基于网络的防御和工具可能在短期内有一定的效果,但通常都没有太大作用,而且工作量非常大。
-
网络防御的效果不大。网上流传着一个梗,一张经过 PS 的汽车照片,其车牌号上包含了一条注入工具。这个梗的点就在于,开发人员都知道,车牌号会通过计算机视觉分析后记录到日志中。构成注入的数据并没有出现在网络层。同样,大多数应用程序使用数据的不同部分,解码数据并记录各种信息。任何网络工具都无法匹配足够多的模式来检测安全问题。
-
通过观察和追踪来阻止攻击者 IP 的方法并不是特别有效。虽然有些团队可能会维护一张攻击者的列表,但 AWS IP 之所以被称为弹性 IP,是因为这些 IP 会定期变化,因此,即便你阻止了某个 IP,过一段时间可能就又解锁了,或者很快就会受到不同 IP 的攻击。
系统属性和动态补丁的效果一般
有几个补丁和系统属性可以控制 log4j2 的行为并阻止攻击。有时候,有些库无法及时更新,或者团队在努力更新依赖项,但需要一定的时间,在这两种情况下,就可以考虑这几个补丁和系统属性。
相关的 Java 系统属性有两个:
-
-Dcom.sun.jndi.rmiobject.trustURLCodebase=false
-
-Dcom.sun.jndi.cosnaming.object.trustURLCodebase=false
将这两个属性设置为 false 可以阻止远程攻击。
还有一个动态的补丁,它可以连接到正在运行的 JVM 并给其打补丁。这个补丁必须在 JVM 每次启动时应用。虽然这两种方法有一定的作用,但相较而言,更新库更简单。
Java 如何处理日志
Java 开发人员一般可以从多个日志系统和记录方式中进行选择。多年来,随着社区的发展,许多日志框架也可以协同工作了:
-
System Logger(2017 年推出,推荐)是 JDK 9 中引入的日志系统。它改进了 JDK Logger 的 API,并提供了类似于 SLF4j 的记录方式,可以将 JDK 的日志重定向到应用程序团队选择的日志系统。
-
JDK Logger(2004 年推出)是 Java 1.4 中引入的日志系统。由于 JDK 中大量使用了该日志系统,因此变得很流行,但是 API 有点蹩脚。虽然这个日志系统也不错,但不如其他框架。
-
Log4j 和 Log4j2 是社区推荐的日志系统,二者改进了 API,因此开发团队可以更轻松地控制记录的内容以及各个级别的日志记录数据的方式。
-
Logback 和 SLF4j 也是流行的日志系统。SLF4J 是一个简单的日志记录方式,可帮助团队处理许多日志记录,库的维护人员可以将日志输出到 SLF4J,然后由应用程序开发人员配置他们将使用哪些底层的日志系统来统一输出。此外 SLF4J 还建立了良好的 API,最大限度地减少了依赖关系。
-
JBoss Logger 是 JBoss 生态系统中的另一个流行的日志系统。该系统性能良好,而且运行速度很快。如今它还支持其他流行框架,如 Quarkus。
-
Apache Commons-Logging(2002 年推出)诞生于 JDK 日志系统之前,并启发了许多 API。它的最后一个版本是于 2014 年推出的,之后人们陆续开始采用其他以支持良好的日志记录为目标的 API。
2022年推荐的日志系统
依赖项越少越好,项目越新越好,因此我们可以考虑 System Logger。
有些项目拥有大量依赖项,但优势在于它们使用的日志记录工具与大多数的依赖项相同,只不过选用了其他日志记录方式。
如果你没有任何日志系统,则可以考虑 System Logger,它是一款拥有良好 API 的 JDK 日志记录工具。
日志系统到底是做什么的?
日志框架能够让应用程序的主人看到通用格式的日志消息、时间戳、线程名称以及其他数据。
此外,团队可以将不同的输出重定向到不同的位置,甚至无需显示出来,例如,你可以将访问日志发送到一个文件,将系统报告发送到其他地方,然后选择显示所有级别的日志信息,或者动态查看某个库的调试信息。