OpenJDK 的安全改进使 Java 对用户更加安全,并帮助软件工程师简化了开发流程。修补漏洞、减少攻击面和维护社区都有助于实现这一目标。在这篇博文中,Erik Costlow 解释了这些改进以及 Azul 如何帮助提高开发人员的工作效率。
在过去的十年中,OpenJDK 社区在简化软件工程师开发流程的同时,也大大提高了 Java 对用户的安全性。OpenJDK 安全改进的综合结果是,安全问题减少了,处理所有软件中不可避免的安全问题的流程也更清晰了。
具体来说,有三点有助于实现这一改进:
- 安全的 Java 开发工具包 (JDK),能及时修补漏洞并按计划发布。
- 模块化的 JDK,通过创建信任边界并帮助删除不必要的模块来减少攻击面。
- 管理良好的社区,对软件库具有明确的所有权。
及时更新的安全 JDK
Java 开发工具包按照事先公布的计划每年发布四次。每次发布都会进行标准改进,并修补任何新的安全风险。专门的 OpenJDK 漏洞小组会对漏洞进行分类并进行优先级排序,以便能够以适当的速度修复这些漏洞。与报告漏洞的研究人员进行的这种协调,确保了补丁能够及时得到应用。
安全的 JDK 并不意味着不会出现漏洞;它意味着一旦出现漏洞,就能及时、正确地解决。
计划安排的补丁
安全补丁和改进程序每年发布四次,均在预先公布的日期发布。通过这些日期,项目经理和其他人员可以将 Java 补丁应用安排为已知的季度活动。用户可以在每三个月的第三个星期二或之后安排时间,而不是订阅电子邮件列表并对警报做出回应:一月、四月、七月和十月。
处理和修补漏洞的一个关键部分是跟踪漏洞并告知人们。社区工作的一个关键部分是对发现安全问题并适当披露这些问题以帮助开源项目的研究人员给予认可。
常见漏洞和暴露 (CVE) 公告
OpenJDK 漏洞小组维护着每个 Java 版本中已修补的 CVE 的列表,并对那些以负责任的态度进行漏洞披露的不同研究人员表示感谢。这为下游 Java 用户提供了回答简单问题的能力:如果我使用以前版本的 Java,这样做会有什么风险?
OpenJDK 漏洞小组在这些公告中所做的另一项出色工作是按组件跟踪 CVE,因为并非所有 Java 用户都使用每个组件。
简而言之,仅仅说旧版 Java 安装包含 CVE 并不足以说明该系统存在该漏洞。
一致的命名
2019 年初,Docker 上的主要 OpenJDK 发行版提供的是“神秘肉 Java”,这是一个与修补程序计划不同步的源代码版本,错过了各种安全修补程序,但仍然使用了包含安全漏洞的 JDK 版本号。结果,Docker 映像的用户是不安全的,而已知下游 Java 分发的用户是安全的。
通过认识到漏洞披露的必要性并使用预先发布的季度修补程序节奏,在 OpenJDK 上工作的 Java 供应商通过 JEP 322(基于时间的发布版本)围绕相同的编号方案进行工作。通过协调围绕季度修补程序计划的版本号,大多数 Java 供应商使用相同的数字,这些数字可以相互理解和比较,以了解哪个版本发布较晚以及该版本中存在哪些安全修补程序。
这种协调的替代方案将是半可预测但毫无意义的数字,这需要特殊的查找表来了解哪些安全修补程序存在于哪里。
基于模块的 JDK 减少了攻击面
Java 8 及以下版本的原始文档提供了一个概念图,说明某些功能位于何处。从威胁建模的角度来看,这也是漏洞存在的位置。
一些片段很容易识别:
- 驱动下载或执行的风险完全集中在部署插件中,该插件在现代 Java 运行时环境 (JRE) 中不再可用。
- 只有当应用程序使用 Java 数据库连接 (JDBC) 时,才会发生 SQL 注入。不使用 SQL 的应用程序不会受到 SQL 注入的攻击。
- 针对 XML(如 XXE)的攻击发生在 XML JAXP 区域内。
- 反序列化攻击源于反序列化组件和 IO 结构。
开发人员和运行应用程序的人员从中受益,因为他们可以查看应用程序并缩减需要担心的内容,只关注正在使用的部分。这突显了 OpenJDK 漏洞小组季度安全公告中模块信息的重要性。
开发人员可以查看这些模块并检查每个模块中与其应用程序相关的风险。例如,敏感数据和暴露风险;这在日志记录模块中很常见,但在像 Image/IO 这样的模块中则少得多。核心好处是简单地了解存在的风险以及风险所在的位置,而不是担心模糊且无法识别的威胁,以及防御措施部署在错误的位置。
另一点是要认识到概念图中没有出现什么,并关注其出现的地方。例如,在核心 Java 图表和模块中,没有“Web”或 HTML 的概念。这意味着针对 Web 威胁(例如跨站点脚本 (XSS))的工作属于提供该功能的框架或工具。
通过移除模块来降低风险
黑客无法攻击不存在的东西。与其防御应用程序免受无数威胁的攻击,不如简单地移除不需要的模块,这可以 100% 缓解任何需要该模块的威胁。
一个概念图展示了这些模块的外观以及它们之间的关系。创建应用程序或服务的开发人员可以使用 jlink 等工具创建自定义 JRE,从而移除某些模块。
- 移除 JDBC(数据库连接)将阻止 SQL 注入攻击,因为数据库查询将不再存在。
- 移除 ImageIO 将阻止任何与图像相关的漏洞,例如 CVE-2020-14562。该 CVE 存在于 ImageIO 中,因此当移除 ImageIO 时,攻击面就不复存在了。
- 移除 XML JAXP 区域可以减轻 XXE 漏洞,因为 XML 不再存在。(XML 在 Java 9 中已弃用,在 11 中已移除,因此现在这是默认设置。)
使用模块化时,JDK 的默认安全性要高得多。标记为“易受攻击”的 JDK 版本实际上可能并不易受攻击。
这种区别至关重要,因为许多软件团队都需要以开源依赖项的形式进行漏洞扫描。扫描程序通常只查看库名称,然后假设所有使用的库都包含所有 CVE。对于自定义 jlink-ed JRE,任何报告 JRE 易受此缺陷攻击的扫描程序都是不正确的。移除组件会移除该组件中的漏洞和风险。黑客无法攻击不存在的东西。
受管理的社区明确拥有软件库
大多数 Java 依赖项来自 Maven Central,其中的库由三部分组成:组、构件和版本。通过提供明确的拥有级别,Sonatype(运行 Maven Central)创建了一个环境,使 Java 开发人员无需担心其他社区中发生的依赖关系混淆之类的攻击。
所有 Maven 依赖项都由三部分组合标识:组、构件和版本。组表示命名空间,通常使用 Java 的反向 DNS 样式。要发布构件,开发人员需要与命名空间有一定的连接。例如,为 example.com 工作的开发人员无法在他们没有关联的已建立组(不是 com.example)下发布构件。
此外,拥有一个主要的库来源可以阻止另一个系列从互联网上的随机位置获取组件,这些位置的域会过期并被出售。这种情况发生在 JavaScript 社区中,一家公司收购了 Polyfill,并将原始库替换为立即提供给数十万个网站的新代码。当代码被交换或组不完全匹配时,Java 使检测变得非常容易。
战术要点
归根结底,团队可以采取三个行动来确保其应用程序和系统保持安全。
- 根据发布的计划修补 Java,理想情况下为每季度一次。
- 使用模块化来减小其应用程序的攻击面。
- 定期从可信来源修补依赖项。
Azul 如何增强 OpenJDK 安全改进措施
Azul Intelligence Cloud 增强了 openJDK 的安全改进措施,并从生产 Java 运行时数据中提供可付诸行动的智能,以便有效地识别出可删除的未使用代码和无效代码,并对存在漏洞的代码按优先顺序进行修复。它可与任何供应商或发行版提供的任何 JVM 配合使用,从而大幅减少企业整个 Java 环境中因无效率任务所耗费的时间,进而显著提升 DevOps 的生产力。
代码目录对生产中的代码进行编目
Code Inventory® 是唯一能够对企业所有 Java 工作负载在生产环境中所运行的代码进行精确编目的解决方案。它减少了维护和测试无效代码和未使用代码的时间和负担,显著提高了开发人员的生产力,并最终节约了成本。
Azul Vulnerability Detection 发现生产环境中的已知漏洞
Azul Vulnerability Detection 可检测实际使用(而非仅仅存在)的漏洞代码,这消除了误报,实现了高效的漏洞分类。通过检测生产环境中使用点的漏洞,它填补了软件供应链终端的关键空白,并增强了传统工具的功能,从而获得更准确的结果。
本文原载于《The New Stack》。