PyPI新一代数字证明:重塑Python软件供应链安全

本文深入解析PyPI基于PEP 740实现的新一代数字证明技术,探讨其如何通过Sigstore和可信发布机制实现密码学可验证的软件来源追溯,涵盖技术架构、工作流程以及未来客户端验证的发展方向。

证明: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博客上的官方公告

从可信发布到Sigstore

可信发布将PyPI托管的项目与处理发布的密码学可验证机器身份(例如github.com/example/example的release.yml)连接起来。这不仅消除了手动API令牌流程,还提供了更基础的东西:来源!

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

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

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

这就是Sigstore的用武之地。Sigstore通过免费、公开可访问、可审计的证书颁发机构(Fulcio)将短期签名密钥绑定到机器身份。Fulcio接受OIDC凭证形式的机器身份,这意味着PyPI的可信发布流与Sigstore签名隐式兼容:可信发布者只需向Fulcio提交带有OIDC凭证的证书签名请求,并接收用于后续使用的签名证书。

Fulcio将OIDC凭证中的适当声明嵌入公共证书中,为我们提供了公开可验证的来源,无需披露凭证本身或单方面信任PyPI正确提供它!

从Sigstore到证明和来源

Sigstore通过提供公共可验证凭证(X.509证书形式)缩小了可信发布与来源之间的差距,该凭证将临时密钥对绑定到机器身份(例如发布到PyPI的GitHub存储库和工作流)。

然而,还剩一步:Sigstore颁发的证书绑定到可信发布身份,但本身并不签署发布内容(即实际的Python包分发)。为覆盖后者,我们需要使用临时密钥对对包分发的证明进行签名,密码学将分发自身身份(其名称和摘要)绑定到其来源(实际生产它的GitHub存储库或其他源)。

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

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

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

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

现状如何?

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

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

为深入了解后者,我们构建了Are We PEP 740 Yet?,跟踪PyPI上360个最下载包对PEP 740证明的采用情况:

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

未来方向

所有这些工作中明显缺少一件事:下游验证。如规范所述,PEP 740仅涉及索引本身:它告诉PyPI如何接收和验证证明以供自身使用,以及如何在公共索引端点上重新分发它们,但并未强制(甚至定义)安装客户端(如pip和uv)的验证流程。

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

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

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

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

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

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

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

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