可信发布:提升软件包安全性的新标准
可信发布:入门指南
可信发布本质上是一种认证机制。与密码或长期API令牌类似,它向索引提供身份证明和权限声明,索引验证通过后允许执行相应操作。其独特之处在于无需预共享密钥即可完成认证。
OpenID Connect与“环境”凭证
可信发布基于OpenID Connect(OIDC)构建,这是一个基于OAuth2的开放身份验证标准。OIDC允许身份提供商(IdP)生成可公开验证的凭证(实际为JWT令牌),证明特定身份(如GitHub仓库或CI运行实例)。关键突破在于:OIDC身份不仅限于人类用户,可以是机器标识、源码仓库或特定CI运行实例,且凭证可通过“环境”方式提供(仅目标身份可访问)。
主流CI平台已支持此功能:
- GitHub Actions于2021年底添加环境OIDC凭证支持
- GitLab于近期添加相同功能
通过OIDC令牌,可精确验证身份信息(包括GitHub仓库、工作流名称、触发用户等),这些信息可成为认证系统的约束条件。
信任是一切
OIDC提供验证身份证明的机制,第三方服务(如PyPI)可接受OIDC令牌并基于其分配权限。由于OIDC令牌与IdP公钥加密绑定,攻击者无法伪造令牌。
关键问题:如何将OIDC身份映射到特定PyPI项目?这需要信任设置:
- 用户在PyPI上配置项目与发布者(OIDC身份)的信任关系
- 配置仅需一次,且仅涉及一方(无需向CI提供商共享令牌)
- 信任设置包含完全公开的信息(声明值集合)
以GitHub Actions发布到PyPI为例,信任设置包括:
- GitHub用户/仓库标识
- 发布工作流文件名(如release.yml)
- (可选)GitHub Actions环境名称
看,没有密钥!
完整发布流程(GitHub示例):
- 开发者触发GitHub Actions发布工作流
- 执行正常构建过程
- 自动化获取当前工作流的OIDC令牌(通过GitHub Actions OIDC IdP)
- 向PyPI发送OIDC令牌
- PyPI验证令牌并交换为短期API令牌(仅限信任该声明的项目)
- 返回短期API令牌
- 工作流使用短期令牌执行正常发布步骤
99%的发布者无需关注步骤3-7:官方PyPA GitHub Action已封装这些细节。
为什么需要关注?
即使您已遵循最佳实践(使用最小权限令牌、存储为工作流密钥、审核发布流程),传统方法仍存在缺陷:
可用性问题:
- 手动管理API令牌繁琐(特别是单仓库多包场景)
- 工程师时间可更有效利用
预入侵安全:
- 长期令牌对所有获取访问权的攻击者均等脆弱
- 可信发布减少攻击面:仅特定环境/步骤无法生成OIDC凭证
入侵后恢复:
- 长期凭证回收缓慢且易出错
- 可信发布凭证仅存活数分钟,丢失访问权即自动隔离
安全与威胁模型考量
可信出版需针对威胁模型设计,既要解决现有认证方法的不足,也要应对新暴露的攻击场景。
现有威胁:账户接管和供应链攻击
账户接管(ATO)是包装生态中的已知问题。可信发布可减少ATO范围:未来包索引允许包选择仅可信发布时,包索引本身的ATO不会允许攻击者上传恶意版本。
供应链安全方面:无可信发布时,GitHub Actions需信任所有执行的第三方操作(它们均可读取配置密钥)。可信发布正针对此关键攻击模型进行防护。
新威胁:“账户复活”和恶意提交者
账户复活攻击:
- 用户更改用户名后,攻击者可能接管旧用户名
- PyPI项目可能突然开始信任攻击者控制的身份
- 解决方案:GitHub添加稳定用户ID声明,PyPI据此拒绝攻击者令牌
信任模型分化:
- 是否信任所有项目成员作为发布者?
- 许多项目有低活跃度成员(可能未遵循安全最佳实践)
- PyPI实现支持可选GitHub Actions环境,为提交者与发布者不完全重叠的社区提供额外限制
即将来到您附近的包索引
PyPI是目前唯一提供可信发布的包索引,但该技术可轻松适配其他生态(如Rust的Crates、Ruby的RubyGems、JavaScript的NPM等)。我们相信,如同2019年的双因素认证,可信发布方案将成为开源包装安全模型的关键组成部分,并为后续改进(如生成PyPI版本源自特定源工件的加密证明)奠定基础。
如果您或您的公司对此感兴趣,欢迎联系我们!我们在开源生态系统安全功能方面拥有多年经验,始终寻找为关键开源项目和服务贡献的更多方式。