Astral(开发 Ruff 和 uv 的团队)最近公开分享了他们在开源项目安全实践上的完整方案,值得所有开源维护者参考。近年来供应链攻击频发,Trivy、LiteLLM 等知名项目相继中招,开发者越来越关心自己依赖的工具是否真的安全。
本文整理编译了 Astral 的实践经验,供国内开源项目参考。
Astral 所有开发流程都依赖 GitHub Actions,但他们认为 GitHub Actions 默认配置非常不安全,很多已知攻击都源于糟糕的默认设置。他们采取了以下措施:
1. 禁用危险触发器
Astral 全面禁用了 pull_request_target 和 workflow_run 这两个触发器。这两个触发器几乎不可能安全使用,攻击者总能找到滥用方法。
经验:大多数项目以为自己需要这些触发器,但实际上绝大多数场景都可以用权限更低的 pull_request 替代,或者直接把信息放在工作流日志中,用 Job Summaries 功能展示。
如果确实需要第三方 PR 评论这类功能,Astral 建议完全离开 GitHub Actions,使用独立的 GitHub App 或 webhook 处理。
2. 所有 Action 必须固定到具体 commit
标签和分支是可变的,所以所有 Action 都必须固定到完整的 commit SHA,而且要交叉验证这些 commit 确实对应真实的发布状态,防止"伪装 commit"攻击。
他们用两种方式保证:
- • 使用 zizmor 工具进行
unpinned-uses 和 impostor-commit 审计 - • 开启 GitHub 原生的"要求所有 Action 固定到完整 commit SHA"策略,这是硬性关卡
即便如此,这还不够——hash-pinning 只保证 Action 内容不可变,但 Action 内部仍然可能下载可变内容(比如从 GitHub Release 安装最新版二进制)。这类"不可变性缺口"目前需要人工审查发现,并与上游合作修复。
修复方式很简单:在 Action 中把二进制下载 URL 和密码哈希绑定,哈希成为 Action 不可变状态的一部分,攻击者无法篡改指向二进制的可变指针。
3. 最小权限原则
- • 组织级别默认为只读权限
- • 每个 workflow 起步都是
permissions: {},只在需要的 job 上逐步开放权限 - • 尽可能使用部署环境+环境专属密钥,隔离密钥范围,避免测试或代码检查作业拿到发布密钥
4. 工具支持
他们使用:
- • zizmor:GitHub Actions 静态分析
- • pinact:自动固定 Action 版本
二、仓库和组织安全:层层设防
除了 CI/CD,Astral 在账户和仓库层面也做了很多限制:
| 安全措施 | 具体做法 |
|---|
| 限制高权限账户 | 只有必要人员才有管理员或高特权角色,大多数成员只对需要工作的仓库有读写权限 |
| 强制强 2FA | 要求所有成员使用不弱于 TOTP 的 2FA 方法,如果 GitHub 允许,未来会只允许钓鱼抵抗型方法(WebAuthn、Passkey) |
| 全组织分支保护 | main 分支不能强制推送,必须走 PR;禁止特定分支模式(如 advisory-*、internal-*)防止安全工作提前泄露 |
| 标签保护 | 发布标签只有在发布部署成功后才能创建,且一旦创建就不能更新或删除,标签本质不可变 |
| 禁止管理员绕过 | 所有保护在组织层面强制执行,就算仓库管理员也不能绕过 |
Astral 公开分享了他们的规则集配置[1],其他人可以直接参考。
三、自动化:安全隔离那些 GitHub Actions 做不好的事情
有些事情 GitHub Actions 能做,但做不安全,比如给第三方 PR 发评论。这种情况下,Astral 使用 GitHub App 模式把这些任务安全隔离在 GitHub Actions 之外:
- • GitHub 发送 webhook 事件给 GitHub App,GitHub App 在独立环境处理,控制权更大
- • GitHub App 虽然不能消除所有敏感凭证,但环境不像 GitHub Actions 那样到处混合代码和数据
- • template injection 这类攻击对 GitHub App 很难生效,但仍然需要像开发其他应用一样保持安全思维
提示:GitHub App 模式对一人项目和爱好者项目来说确实复杂,Astral 希望未来大公司和大项目能推动可用性改进。
Astral 推荐了 Mariatta 的教程[2]适合想要学习 Python 开发 GitHub App 的人,他们计划未来开源自己的 astral-sh-bot。
四、发布安全:多环节保障分发渠道安全
除了 GitHub,用户还通过 PyPI、Homebrew、Docker 镜像等渠道安装 Astral 的工具,每个环节都需要单独考虑:
1. 尽可能使用可信发布(Trusted Publishing)
这种技术消除了对长期凭证的需求,大大减少了凭证泄露导致的项目接管风险。
2. 使用 Sigstore attestations
对于二进制和 Docker 镜像发布,生成基于 Sigstore 的证明,密码学可验证发布产物确实来自 Astral 的真实发布流程。
3. 使用 GitHub 不可变发布特性
防止攻击者事后替换已发布的构建——Trivy 攻击就是攻击者通过强制推送覆盖旧标签引入恶意版本。
4. 发布流程不使用缓存
防止攻击者通过缓存投毒攻击危害我们的构建。
5. 多重发布保护
- • 发布环境隔离:只有在发布环境中的作业才能访问发布密钥,测试和 lint 作业拿不到
- • 双人审批:激活发布环境必须至少有另一名授权成员批准,攻击者需要同时攻破两个强 2FA 保护的账户
- • 标签保护:在发布部署成功之前不允许创建发布标签,防止攻击者绕过正常流程直接发布
五、依赖安全:主动管理上游风险
像几乎所有现代软件一样,Astral 的工具也依赖第三方依赖生态,他们采取这些措施来度量和缓解上游风险:
- 1. 使用 Dependabot 和 Renovate 保持依赖更新,发现已知漏洞
- 2. 采用冷却期:新版本发布后不立即更新,因为此时依赖被攻陷的可能性最大,uv 原生也支持这个特性
- 3. 社交连接和贡献:与上游维护者保持联系,定期做安全贡献,包括修复他们自己的 CI/CD 和发布流程
- 4. 保守添加新依赖:能删则删,尽量不引入二进制 blob,仔细审查依赖功能,禁用不需要的功能
- 5. 资金支持:通过 OSS Fund 经济赞助他们依赖的项目,推动开源生态整体发展
总结:开源安全的核心要点
Astral 总结说,开源安全是个难题,攻击者在不断进化,防御也必须随之进化。值得重点关注的要点:
- 1. 认清 CI/CD 的边界:什么都想放 CI/CD 很诱人,但有些事情 GitHub Actions 就是做不安全,最好完全放弃,或者用 GitHub App 隔离出去
- 2. 不要矫枉过正:不是要完全抛弃 CI/CD,CI/CD 本身对安全态势很重要,比起完全不用托管 CI/CD,付出努力加固 GitHub Actions 是值得的
- 3. 隔离并消除长期凭证:泄露后横向传播最常见方式就是滥用长期凭证,尽可能完全消除(比如用 Trusted Publishing 或其他基于 OIDC 的认证机制)
- 4. 不能消除就最小化范围:把凭证放在特定部署环境,附加激活要求,只发放完成任务所需的最小权限凭证
- 5. 加强发布流程:在 GitHub 上使用部署环境、审批、标签分支规则集、不可变发布,减少账户被接管后攻击者的操作空间
- 6. 保持依赖感知:维护依赖树整体健康状况对理解自身风险至关重要,用工具加人工,保持依赖安全,也帮助上游维护他们自己的流程
Astral 也坦言,本文提到的很多技术他们还在评估中,未来几周和几个月肯定还会调整和加强——安全是个持续过程,不是一劳永逸。
本文基于 Astral 官方博客《Open source security at Astral》编译整理,原文链接:https://astral.sh/blog/open-source-security-at-astral
引用链接
[1] 规则集配置: https://gist.github.com/woodruffw/81540e1e1087b60e7543fda2f230fc0f
[2] Mariatta 的教程: https://mariatta.ca/building-a-github-app.html