深入探索Homebrew构建溯源技术,提升软件供应链安全

本文详细介绍了Homebrew构建溯源技术的实现,包括如何通过密码学验证软件包的真实性,利用GitHub Actions生成构建证明,以及如何通过brew verify命令进行验证,有效防范供应链攻击。

深入探索Homebrew的构建溯源

Homebrew快速回顾

Homebrew是macOS和Linux的开源包管理器。其核心是homebrew-core,一个包含超过7000个精选开源包的默认仓库。homebrew-core的包每年被下载数亿次,构成了使用macOS进行开发的程序员的基础工具套件(如node、openssl、python、go等)。

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

成功构建和测试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及其校验和,导致所有用户从此 silently compromised installs。

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

构建溯源

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

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

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

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

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

我们的实现

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

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

尝试重建所有旧的bottles。这在技术上和物流上是不可行的,既因为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证明的尾巴随着时间的推移而减少,随着公式转向新版本。一旦所有可到达的bottles完全转向,Homebrew将能够完全移除回填检查并断言完美的溯源覆盖!

今天验证溯源

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

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

一个专用的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 verify和brew install都在内部包装了gh CLI,并且如果gh尚未安装,将在本地引导gh。我们打算在中期用纯Ruby验证器替换我们对gh证明的使用。

构建溯源测试版依赖于经过身份验证的GitHub API端点,意味着gh必须能够访问合适的访问凭证。如果您在使用brew verify或brew 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的其余成员,感谢他们对更好、更安全的软件供应链的愿景和支持。

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