深入探索Homebrew构建来源验证:保障软件供应链安全

本文详细介绍了Homebrew如何通过密码学构建来源验证技术增强软件供应链安全,包括实现原理、技术架构及验证方法,有效防止恶意软件注入和供应链攻击。

窥探Homebrew的构建来源验证 - The Trail of Bits博客

Joe Sweeney, William Woodruff
2024年5月14日
密码学, 研究实践

去年11月,我们宣布与Alpha-Omega和OpenSSF合作,为Homebrew添加构建来源验证功能。

今天,我们很高兴地宣布这项工作的核心部分已经上线并进入公开测试阶段:homebrew-core现在对所有在官方Homebrew CI中构建的bottle进行密码学认证。您可以使用我们(目前是外部版本,但即将上游化)的brew verify命令来验证这些认证,该命令可以从我们的tap安装:

这意味着从现在开始,每个由Homebrew构建的bottle都将附带一个密码学可验证的声明,将该bottle的内容绑定到产生它的特定工作流和其他构建时元数据。这些元数据包括(但不限于)产生该bottle的工作流的git提交和GitHub Actions运行ID,使其成为符合SLSA Build L2的认证:

实际上,这为Homebrew构建过程注入了更大的透明度,并通过使普通用户无法被欺骗安装非CI构建的bottle,减少了由受损或恶意的内部人员构成的威胁。

这项工作仍处于早期测试阶段,涉及Homebrew和GitHub内部仍在积极开发的功能和组件。因此,我们不建议普通用户立即开始验证来源认证。

但对于冒险者来说,请继续阅读!

Homebrew快速回顾

Homebrew是macOS和Linux的开源包管理器。Homebrew的瑰宝是homebrew-core,这是一个默认仓库,包含超过7,000个精选的开源包,默认与Homebrew的其他部分一起提供。homebrew-core的包每年被下载数亿次,并为使用macOS进行开发的程序员形成了基础工具套件(node、openssl、python、go等)。

Homebrew的核心功能之一是使用bottle:每个包的预编译二进制分发,可加速brew install并确保其在各个机器之间的一致性。当一个新的公式(描述如何构建包的可机读描述)被更新或添加到homebrew-core时,Homebrew的CI(通过BrewTestBot编排)自动触发一个过程来创建这些bottle。

bottle成功构建和测试后,就是分发的时候了。BrewTestBot获取编译的bottle并将其上传到GitHub Packages,这是Homebrew为homebrew-core选择的托管服务。这一步确保用户可以通过Homebrew的命令行界面直接访问和下载最新的软件版本。最后,BrewTestBot更新对更改公式的引用以包括最新的bottle构建,确保用户在下一次brew update时收到更新的bottle。

总之:Homebrew的bottle自动化通过将人类从软件构建过程中移除,提高了homebrew-core的可靠性。这样做,它也消除了一种特定类型的供应链风险:通过将bottle构建从单个Homebrew维护者转移到Homebrew CI,它减少了维护者受损的开发机器被用来对更大的Homebrew用户群发起攻击的可能性¹。

同时,该方案还有其他方面可能被攻击者利用:具有足够权限的攻击者可能直接将恶意构建上传到homebrew-core的bottle存储,可能利用警报疲劳欺骗用户安装,尽管存在校验和不匹配。更令人担忧的是,受损或恶意的Homebrew维护者可能秘密替换bottle及其校验和,导致所有用户从此静默安装受损的软件。

这种情况是软件供应链中的一个独特但严重的弱点,构建来源验证可以很好地解决这个问题。

构建来源验证

简而言之,构建来源验证提供了密码学可验证的证据,证明一个软件包确实是由预期的“构建身份”构建的,而不是被有特权的攻击者篡改或秘密插入的。实际上,构建来源验证提供了强密码学摘要的完整性属性,结合了一个断言,即该工件是由一个可公开审计的构建基础设施产生的。

在Homebrew的情况下,那个“构建身份”是一个GitHub Actions工作流,意味着每个bottle构建的来源都证明了有价值的元数据,如GitHub所有者和仓库、触发工作流的分支、触发工作流的事件,甚至工作流运行的确切git提交。

这些数据(以及更多!)被封装在一个机器可读的in-toto声明中,使下游消费者能够对单个认证表达复杂的策略:

构建来源验证和更一般的来源验证不是万能的:它们不能替代应用程序级别的保护措施来防止软件降级或混淆攻击,也不能防止“与撒旦的私人对话”场景,即软件本身是恶意的或受损的。

尽管如此,来源验证是可审计供应链的一个有价值的构建块:它通过将攻击者承诺在公开可验证的时间线上的公共工件上,迫使攻击者公开行动,并减少了攻击者可以隐藏其有效负载的不透明格式转换的数量。这在像最近的xz-utils后门这样的案例中尤其突出,攻击者利用上游源仓库和后门tarball分发之间的脱节来保持其攻击的隐蔽性。或者换句话说:构建来源验证不会阻止完全恶意的维护者,但会迫使他们的攻击公开以供审查和事件响应。

我们的实现

我们为Homebrew实现的构建来源验证建立在GitHub的新工件认证功能之上。我们被给予了早期(私人测试版)访问该功能的权限,包括generate-build-provenance操作和gh认证CLI,这使我们能够快速迭代一个可以轻松集成到Homebrew预先存在的CI中的设计。

这为我们提供了所有当前和未来bottle构建的构建来源验证,但我们遇到了一个问题:Homebrew有一个很长的“尾巴”,即预先存在的bottle,这些bottle仍然在公式中被引用,包括在GitHub Actions²不再支持的(架构,操作系统版本)元组上构建的bottle。这个尾巴被广泛使用,给我们留下了一个困境:

尝试重建所有旧的bottle。这在技术上和物流上都是不可行的,既因为GitHub Actions自身支持的运行器的变化,也因为macOS版本之间的重大工具链变化。

仅在有构建来源验证时验证bottle。这将在构建来源验证的预期安全合同中打一个洞,允许攻击者通过剥离任何来源元数据来降级到较低程度的完整性。

这两种解决方案都不可行,因此我们寻求第三种。我们决定既不重建世界也不选择性验证,而是创建一组回填的构建认证,由一个完全不同的仓库(我们的tap)和工作流签名。有了每个bottle背后的回填认证,验证看起来像一个瀑布:

我们首先检查与“上游”仓库绑定的构建来源验证,具有预期的工作流,即Homebrew/homebrew-core与publish-commit-bottles.yml。

如果“上游”来源验证不存在,我们检查在指定截止日期之前来自回填身份的回填认证,即trailofbits/homebrew-brew-verify与backfill_signatures.yml。

如果两者都不存在,那么我们产生一个硬失败。

这给了我们两全其美:回填允许我们在没有来源验证或认证时统一失败(消除降级),而不必重建每个旧的homebrew-core bottle。截止日期然后增加了一层额外的保证,防止攻击者尝试使用回填认证注入意外的bottle。

我们期望回填bottle认证的尾巴随着时间的推移而减少,因为公式转向新版本。一旦所有可访问的bottle完全转向,Homebrew将能够完全移除回填检查并断言完美的来源覆盖!

今天验证来源验证

如上所述:此功能处于早期测试阶段。我们仍在解决已知的性能和用户体验问题;因此,我们不建议普通用户尝试它。

尽管如此,冒险的早期采用者可以通过两种不同的界面尝试它:

一个专用的brew verify命令,可通过我们的第三方tap获得

一个早期的上游集成到brew install本身。

对于brew verify,只需安装我们的第三方tap。安装后,brew verify子命令将变得可用:

1
2
3
4
brew update
brew tap trailofbits/homebrew-brew-verify
brew verify --help
brew verify bash

展望未来,我们将与Homebrew合作,将brew verify直接上游化到brew作为一个开发者命令。

对于brew install本身,在您的环境中设置HOMEBREW_VERIFY_ATTESTATIONS=1

1
2
3
brew update
export HOMEBREW_VERIFY_ATTESTATIONS=1
brew install cowsay

无论您选择如何尝试这些新功能,某些注意事项适用:

brew verifybrew install都在内部包装了gh CLI,如果尚未安装,将在本地引导gh。我们计划在中期用纯Ruby验证器替换我们对gh认证的使用。

构建来源验证测试版依赖于经过身份验证的GitHub API端点,意味着gh必须具有访问合适访问凭据的权限。如果您在使用brew verifybrew install时遇到初始失败,请尝试运行gh auth login或将HOMEBREW_GITHUB_API_TOKEN设置为具有最小权限的个人访问令牌。

如果您在使用brew install时遇到错误或意外行为,请报告它!同样,对于brew verify:请直接将任何报告发送给我们。

展望未来

以上所有内容都涉及homebrew-core,Homebrew公式的官方仓库。但Homebrew也支持第三方仓库(“taps”),这些仓库提供了少数但重要的总体bottle安装。这些仓库也值得构建来源验证,我们有实现这一点的想法!

更远地,我们计划尝试源代码来源验证:Homebrew的公式已经哈希固定其源代码工件,但我们可以更进一步,额外断言源代码工件是由其URL中潜在的仓库(或其他签名身份)产生的,或者嵌入到公式规范中。这将与GitHub的工件认证很好地组合,启用一个假设的DSL:

请继续关注此领域的进一步更新,并且一如既往,不要犹豫联系我们!我们有兴趣在其他开源打包生态系统上进行类似的改进合作,并很乐意听取您的意见。

最后但同样重要的是,我们要感谢Homebrew的维护者在此过程中的开发和审查。我们还要感谢Dustin Ingram对原始提案的撰写和设计,GitHub包安全团队,以及Michael Winser和Alpha-Omega的其他人对更好、更安全的软件供应链的愿景和支持。

¹在不太遥远的过去,Homebrew的bottle是由维护者在自己的开发机器上生产并上传到一个共享的Bintray账户。Mike McQuaid的2023年演讲提供了Homebrew向CI/CD构建过渡历史的优秀概述。

²或者通过自托管运行器容易提供,Homebrew用于某些构建。

如果您喜欢这篇文章,请分享: Twitter LinkedIn GitHub Mastodon Hacker News

页面内容 Homebrew快速回顾 构建来源验证 我们的实现 今天验证来源验证 展望未来 最近帖子 用Deptective调查您的依赖项 系好安全带,Buttercup,AIxCC的评分回合正在进行中! 使您的智能合约超越私钥风险 Go解析器中意外的安全陷阱 我们从审查Silence Laboratories的首批DKLs23库中学到了什么 © 2025 Trail of Bits. 使用Hugo和Mainroad主题生成。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计