PyPI Trusted Publishing + Attestations 实战:告别 API Token,建立可信 Python 发布链
摘要
很多 Python 项目的 PyPI 发布流程仍然是:
GitHub Actions + PYPI_API_TOKEN问题在于:API Token 是长期凭据,一旦 CI Secret、第三方 Action 或 Runner 被攻陷,攻击者可能直接向 PyPI 发布恶意版本。
PyPI Trusted Publishing 通过 OIDC 把“长期 Token”替换成“工作流身份”;同时 PyPI 已支持将 Attestations 与每个发布文件绑定,使 Python 包可以拥有更加完整的发布来源证据。
本文给出一套可直接落地的 Python 包可信发布流程。
1. 传统 PyPI 发布链的问题
典型配置:
password:${{secrets.PYPI_API_TOKEN}}攻击者的目标非常明确:
偷到 Token ↓ 上传恶意版本 ↓ 用户 pip install一个长期 Token 可能跨越:
- 多次发布;
- 多个 Workflow;
- 很长生命周期。
这与现代供应链安全的“短期身份”方向相矛盾。
2. 什么是 Trusted Publishing?
Trusted Publishing 的核心是:
CI Identity ↓ OIDC ↓ PyPI Verify ↓ Temporary Publishing Authorization不需要在 GitHub Secrets 里保存 PyPI 用户名、密码或长期 API Token。
PyPI 当前支持多类 Trusted Publisher,包括 GitHub Actions、GitLab CI/CD、Google Cloud 等。
3. GitHub Actions 中最关键的权限
发布 Job:
permissions:id-token:write这个权限允许 Workflow 请求 OIDC Token。
推荐再使用 GitHub Environment:
environment:name:pypi结合人工审批与保护规则。
4. 一个推荐发布流程
5. 官方 PyPA Action
PyPI 官方文档推荐使用 PyPA 的发布 Action。
示意:
jobs:pypi-publish:runs-on:ubuntu-latestenvironment:pypipermissions:id-token:writesteps:-name:Publish packageuses:pypa/gh-action-pypi-publish@release/v1这样无需:
PYPI_API_TOKEN6. 什么是 PyPI Attestation?
PyPI 允许 Attestation 与单个 Release File 绑定。
例如一个 Release 可能包含:
demo-1.0.0.tar.gz demo-1.0.0-py3-none-any.whl每个文件都可以拥有对应证明。
证明的价值是:
这个文件来自哪个可信发布身份?7. 官方发布 Action 会自动生成证明
根据 PyPI 文档,使用官方:
pypa/gh-action-pypi-publish时,Attestations 默认会自动生成并上传。
这意味着发布链可以自然变成:
OIDC Identity + Trusted Publishing + Attestation8. 手工生成 Attestation
PyPI 提供:
pypi-attestations工具。
例如:
python-mpipinstallpypi-attestations python-mpypi_attestations sign dist/*在 GitHub Actions 中,如果启用了:
id-token:write工具可以使用工作流的环境身份生成证明。
9. Twine 上传 Attestation
如果使用 Twine:
twine upload--attestationsdist/*即可在上传发行文件时携带相应 Attestations。
10. 为什么 Attestation 很重要?
以前:
PyPI 上有这个包大家自然认为:
这是官方发布但现代供应链安全要求更严格:
Package exists ≠ Trusted Build我们更希望验证:
这个文件 来自这个项目 由这个 CI 身份 在这个发布流程中生成11. 建议拆分 Build 与 Publish
不要让:
Build Job同时拥有 PyPI 发布身份。
推荐:
Build Job ↓ Artifact ↓ Publish Job ↓ OIDC ↓ PyPIPublish Job:
- 不执行不可信 PR 代码;
- 只消费已构建的发行文件;
- 只在 Tag/Release 时执行;
- 绑定 Environment。
12. 第三方 Action 风险
Trusted Publishing 消除了长期 Token,但并不代表 Workflow 自动安全。
仍然需要:
- 固定第三方 Action Commit;
- 最小化 Job 权限;
- 防止 PR 注入;
- 限制 Tag 创建权限;
- 保护 Release Workflow。
否则攻击者仍然可能借合法工作流身份发布恶意包。
13. Python 项目的可信发布基线
推荐:
Protected Branch + Protected Tag + Dedicated Release Workflow + OIDC Trusted Publishing + Attestation + SBOM + Reproducible Build如果是高价值软件,还可以增加:
Manual Approval14. 如何迁移旧项目?
第一步
在 PyPI 项目设置中配置 Trusted Publisher。
第二步
给 GitHub Workflow 增加:
permissions:id-token:write第三步
切换到官方发布 Action。
第四步
删除:
PYPI_API_TOKEN第五步
确认 Attestation 正常生成。
15. 安全收益
从:
Secret-based Publishing升级到:
Identity-based Publishing直接减少了:
- Token 泄露;
- Token 轮换;
- Secret 管理;
- Token 跨工作流复用。
同时 Attestation 又增加了发布来源可验证性。
总结
Python 包发布链正在从:
API Token向:
OIDC Identity + Trusted Publishing + Attestation升级。
对于企业 Python 项目,建议把 Trusted Publishing 作为新的默认发布模式,而不是额外增强项。
最终目标是:
每个发布到 PyPI 的文件,都能证明它来自预期的项目和可信发布流程。