PyPI新一代数字证明:构建可验证的软件供应链安全

本文深入解析PyPI基于PEP 740实现的新一代数字证明技术,通过Sigstore和可信发布机制,为Python包提供加密可验证的来源证明,提升软件供应链安全性和透明度。

证明:PyPI上的新一代签名

背景:可信发布

过去一年,我们与Python包索引(PyPI)合作,为Python生态系统开发了一项新的安全功能:索引托管的数字证明,具体规范见PEP 740。

这些证明通过提供关键可用性、索引可验证性、加密强度和来源属性,改进了传统的PGP签名(已在PyPI上禁用),使我们离实现软件供应链的整体加密可验证来源更近一步。

好消息是:如果您已经使用可信发布(Trusted Publishing)向PyPI发布包,可能无需任何更改:官方PyPI发布工作流内置了证明支持,自v1.11.0及更高版本默认启用。换句话说,只要您使用(或升级到)pypa/gh-action-pypi-publish@v1.11.0或更高版本,并配置可信发布者,您的包将默认获得构建来源证明!

默认启用是我们的关键设计约束:我们希望一个证明功能能够与现有发布身份集成,避开传统数字签名设计中反复出现的密钥和身份管理挑战。Sigstore成为这些挑战的解决方案:其基于身份的无密钥签名支持,提供了PyPI对可信发布和包来源支持之间的公开可验证链接。

查看官方PyPI文档获取有关如何创建和使用索引托管证明的实用信息,并继续阅读这里的技术摘要,了解这些证明的工作原理以及我们对其未来的展望!

也请阅读PyPI博客上的官方公告!

背景:可信发布

去年,我们与PyPI合作设计并实现了可信发布,这是一种新的、更便捷、更安全的上传包到PyPI的方式。由于其可用性优势,我们在过去的18个月中看到可信发布取得了巨大成功:超过19,000个独立项目注册了可信发布者,这些项目使用可信发布共同向PyPI发布了近50万个文件:

我们有一篇完整的独立博客文章介绍可信发布和PyPI,但简要总结如下:

  • 可信发布消除了手动配置和范围API令牌的需要。
  • 项目声明批准的可信发布者(GitHub、GitLab、Google Cloud Build、ActiveState等)身份,可以上传新版本。
  • 为确保来自这些身份的请求真实性(即声称是它们的CI/CD工作流),可信发布通过OpenID Connect(OIDC)使用公钥加密。
  • OIDC流程允许可信发布者自动获取PyPI API令牌,无需用户干预,减少了用户错误(如凭据泄露和意外过度范围)的机会。
  • 通过此OIDC流程发布的令牌是短寿命且最小范围的,减少了攻击者囤积它们以供将来使用或使用单个凭据在不同项目之间转换的能力。

PyPI上可信发布的成功也引起了其他生态系统的兴趣:RubyGems在几个月后实现了它,而Rust的crates.io有一个开放的RFC!

从可信发布到Sigstore

可信发布将PyPI托管的项目连接到加密可验证的机器身份(如release.yml @ github.com/example/example)来处理发布。

这对于消除手动API令牌流程非常棒,但它也给了我们更基本的东西:来源!

特别是在GitHub(或GitLab等)打包工作流的上下文中,OIDC凭据中的机器身份给了我们类似“发布来源”的东西:一组关于存储库和工作流状态的声明,对应于包发布到PyPI的时间。

然而,以OIDC凭据的形式,这种来源对外部用户并不立即有价值:

  • PyPI不能共享凭据本身,因为它本质上是秘密材料。即使有适当的控制(过期和固定受众),PII披露和行为不端的JWT验证器的风险太大,无法冒险进行外部(意味着非PyPI)验证。
  • PyPI可以披露凭据中的声明,例如通过发布元数据,效果如“项目sampleproject由从pypa/sampleproject运行的GitHub工作流pypi-publish.yml发布”。这将导致下游用户被迫信任PyPI诚实地提供这些声明。

这就是Sigstore的用武之地。我们还有另一篇完整的独立博客文章介绍Sigstore及其工作原理,但对我们目的的关键部分是Sigstore通过免费、公开可访问、可审计的证书颁发机构(Fulcio)将短寿命签名密钥绑定到机器身份。

Fulcio接受OIDC凭据形式的机器身份,意味着PyPI的可信发布流程隐式兼容Sigstore签名:可信发布者需要做的就是向Fulcio提交带有OIDC凭据的证书签名请求,并接收用于后续使用的签名证书。

Fulcio将把OIDC凭据中的适当声明嵌入公证书中,给我们一个公开可验证的来源,不需要披露凭据本身或单方面信任PyPI正确提供它!

所涉及的步骤可能有点难跟,所以让我们可视化它们。这是Sigstore参与之前的“传统”可信发布流程:

然后,有了Sigstore在循环中:

观察到,虽然流程中多了一个实体(Sigstore),但从用户的角度来看没有任何变化:他们需要的只是他们的一次性可信发布者配置,这来自原始流程。

从Sigstore到证明和来源

Sigstore通过给我们一个公开可验证的凭据(以X.509证书的形式),将短寿命密钥对绑定到机器身份(如发布到PyPI的GitHub存储库和工作流),缩小了可信发布和来源之间的差距。

然而,还有一个步骤:Sigstore颁发的证书绑定到可信发布身份,但它本身并不为正在发布的东西(即实际的Python包分发)签名。

为了覆盖后者,我们需要使用我们的短寿命密钥对为我们的包分发签署证明,加密将分发自己的身份(其名称和摘要)绑定到其来源(实际产生它的GitHub存储库或其他源)。

这就是PEP 740的用武之地。PEP 740通过固定的证明有效负载将Sigstore和可信发布与实际包分发结合,有效负载本身在in-toto证明框架的范围内定义。

这是一个实际证明的示例,为sigstore v3.5.1生成:

这些证明然后由短寿命密钥对的私钥部分签名,本身绑定到X.509证书,以PyPI本身可验证的方式完成分发身份(文件名和摘要)到来源(嵌入X.509证书的OIDC声明)的完整绑定(因为OIDC声明对应于用户注册的可信发布者身份)。

当然,仅仅生成证明是不够的——这些证明还需要存储,以便用户可以自己验证它们!PEP 740也定义了这一点:上传带有证明的分发在JSON简单API中给定一个来源键,在PEP 503索引中给定相应的数据来源属性。

这些字段包含指向“来源”对象的URL,该对象是每个分发的一个或多个证明对象的汇总,以及PyPI用于验证这些证明的可信发布者身份。我们可以深入这些内部回到我们上面的原始有效负载:

这让我们处于什么位置?

截至10月29日,证明对于任何使用PyPA发布动作用于GitHub的可信发布的人来说都是默认的。这意味着大约20,000个包现在可以默认证明其来源,无需任何更改。我们预计这个数字还会随着时间的推移而增加,因为更多项目(尤其是新项目)默认使用可信发布,作为手动配置API令牌的用户友好和更安全的替代方案。

然而,产生证明的包总数只是一个视角,而且可以说是不完整的:包证明的价值与该包的“重要性”密切相关——即依赖它的用户或下游的数量。PyPI不知道项目的依赖关系,但总下载量是项目在生态系统中相对重要性的强代理。

为了深入了解后者,我们构建了“我们达到PEP 740了吗?”,跟踪PyPI上360个最下载包对PEP 740证明的采用:

到目前为止,360个最下载包中有5%上传了证明。但有一个混淆因素:大约三分之二的最下载包自证明启用以来根本没有更新,意味着我们还不知道一旦它们发布新版本,有多少会有证明!

我们从这里去哪里?

所有这些工作中明显缺少一件事:下游验证。

如所指定,PEP 740仅涉及索引本身:它告诉PyPI如何接收和验证证明以供自己使用,以及如何在公共索引端点上重新分发它们,但它不强制(甚至定义)安装客户端(如pip和uv)的验证流程。

实际上,这意味着索引托管证明的短期影响有限:它们引入了PyPI中使用的可信发布者身份的透明度,但下游客户端仍然需要信任PyPI本身诚实地提供证明。

这不是可接受的最终状态(加密证明只有在实际验证时才有防御属性),所以我们正在研究将验证带到单个安装客户端的方法。特别是,我们目前正在为pip开发一个插件架构,使用户能够将验证逻辑直接加载到他们的pip安装流程中。

长期来看,我们可以做得更好:做“一次性”验证意味着客户端不记得哪个身份应该被信任用于哪个分发。为了解决这个问题,安装工具需要对签名身份有“首次使用信任”的概念,意味着如果证明身份更改(或包在版本之间变得未证明),后续安装可以被用户停止和检查。

如果这听起来像锁文件问题,那是因为它就是!我们正在密切关注PEP 751,因为它定义了我们需要存储预期分发身份的元数据格式。一旦Python生态系统开始采用标准化锁文件,我们将能够使用它们存储和验证身份,很像今天如何使用哈希验证分发完整性。

总而言之,在常见默认安装流程在底层验证证明之前,我们还有一段路要走。但与早期索引托管签名的尝试不同,我们很清楚如何到达那里。然而,与此同时,有些群体可以早期利用PyPI新托管的证明:

  • 研究人员:PEP 740证明建立在Sigstore之上,并提供源存储库和包(如它们在PyPI上出现)之间关键可验证的缺失链接。这使它们成为安全和供应链研究的绝佳数据源!
  • 事件响应者:当可用时,证明 drastically缩短和简化事件调查中最烦人和容易出错的部分:跟踪特定工件回其源,准确找出它何时以及如何产生,等等。
  • 完全控制其构建系统的用户:如果您维护一个完全控制其Python包依赖关系(即不使用pip或其他工具进行解析和安装)的开源或专业项目,那么您可能可以直接将证明验证纳入构建过程!查看我们的pypi_attestations文档作为起点。

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

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