题记与说明
版本:终稿(已按独立审读意见逐条修订,共 8 条,处理记录见文末"附录:审读意见处理记录")| 2026-11-01
综合方法:本报告由两份章节文件综合而成——纵轴篇《GitHub Actions 安全发展历程》(2018–2026 攻防演化时间线)与横轴篇《GitHub Actions 安全横向对比》(2026-11-01 时间截面上与 GitLab CI/CD、CircleCI、Jenkins、Azure DevOps、Bitbucket Pipelines、Buildkite 的六维对比)。两章均基于各自会话内的网络检索与官方文档抓取撰写,关键事实附来源链接;无法核验的数字在文中显式标注。
两章矛盾与重复的处理:① GhostAction 的"3 万+仓库 / 约 1900 账号"规模数字,纵轴原记为 Wiz 2025-09-30 的扩大评估、横轴原记为 2026 年 10 月的二手聚合报道,两处按字面互相矛盾(若 2025-09 已达 3 万+,一年后的"累计"不应仍是 3 万+)。本次修订经直接抓取 GitGuardian 2026-10-07 报告(10-13 更新)核实:3 万+ 仓库 / 约 1900 账号是 2026-10-08~09 新一波爆发的数字(约 1900 个被控账号向 30,000+ 仓库推送恶意 workflow),既非 2025-09 的存量评估、也非跨年"累计"口径;原稿所引 Wiz 原文链接本次抓取 404,"Wiz 2025-09-30 扩大评估"无法核验,已从正文移除。全稿统一为三波口径:2025-09 首波(GitGuardian 首轮确证)→ 2026-08~09 第二波 → 2026-10 第三波,详见纵轴·阶段四/阶段五与文末不确定点①。② tj-actions 的 payload 机制,纵轴写"把 secrets 以明文打进构建日志",横轴写"向日志倾倒 runner 内存"——本稿统一为"机制是倾倒 runner 内存,后果是 secrets 以明文进入构建日志"。③ tj-actions 与 GhostAction 的完整叙事两章均有展开,本稿将详细叙事收敛到纵轴·阶段四一处,横轴仅在对比语境下引用并标注"详见纵轴·阶段四",消除重复。
TL;DR
GitHub Actions 八年安全史是一条"便利优先 → 被攻击教会 → 默认收紧"的曲线:2018 年为增长留下三大隐患——默认读写的 GITHUB_TOKEN、把 secrets 带进 fork 上下文的 pull_request_target 逃生门、可被回溯改写的标签引用——外加一项生态风险:公共仓库可绑定 self-hosted runner;2021 年 Codecov 事件、2025 年 tj-actions 与 GhostAction 事件将其逐次击穿。平台以 2023 年 token 默认只读、2026 年执行策略 GA、工作流锁文件 GA、pull_request_target 默认禁用(2026-12-02 强制)完成从"用户开关"到"平台默认"的翻转。横向看,2026 年的 GitHub Actions 在权限粒度、OIDC 联邦、供应链出处(SLSA L3 原生 provenance)上居行业第一梯队,Secrets 完备度被 GitLab 的原生 Secrets Manager 反超,Fork 隔离默认态逊于 Bitbucket/Buildkite;它是攻击最集中的平台,也建了最厚的防护墙——因为生态规模最大。给用户的三句话:权限按 job 最小化并设 org 级默认只读;第三方 Action 优先用工作流锁文件(supersedes SHA pinning),否则钉扎完整 commit SHA;部署走 OIDC 联邦,消灭长期 secrets。各条建议的盘点命令、启用方式、以及 2026-12-02 强制的后果与豁免路径,见 3.3 落地路径。
一、纵轴:发展脉络(2018-10 – 2026-11)
一句话总纲:GitHub Actions 的安全史,本质是一条"便利优先 → 被攻击教会 → 默认收紧"的曲线——2018 年以"自动化工作流"的面目诞生时几乎不考虑 fork 场景的威胁模型;2020–2021 年社区研究和 Codecov 事件迫使平台补上权限、审批、OIDC 等开关;2025 年 tj-actions 供应链攻击与 GhostAction 事件(2025-09 首波,延续至 2026)把"信任第三方 Action"的假设彻底击穿;到 2026 年,GitHub 终于把安全从"用户自己开"翻转为"平台默认关"。
阶段总览
| 阶段 | 时间 | 主题 | 标志性节点 |
|---|---|---|---|
| 1. 诞生与便利优先 | 2018.10–2019 | 抢占 CI/CD 市场 | 有限公测(2018-10-16)、GA(2019-11-13)、自托管 runner(2019-11)、pull_request_target 引入(2019 末–2020) |
| 2. 第一波安全审视 | 2020–2021 | 漏洞研究 + 事件驱动补课 | Pwn Request 研究、Codecov 事件(2021-04)、首次贡献者审批(2021-04)、permissions 键(2021-08)、OIDC(2021-10) |
| 3. 默认安全化与研究深化 | 2022–2024 | 权限模型翻转、攻击面学术化 | GITHUB_TOKEN 默认只读(2022-10 宣布 / 2023-02 生效)、缓存投毒系统化公开(2024-05 深度研究)、PyTorch 攻击验证(2024-01)、ArtiPACKED(2024-08)、Cacheract 缓存蠕虫 PoC(2024-12) |
| 4. 信任链危机 | 2025 | 第三方 Action 失信之年(延续至 2026) | tj-actions 挪标签攻击(2025-03)、CISA 警报、官方推 SHA 钉扎、GhostAction 首波(2025-09) |
| 5. 平台级"默认安全"重构 | 2026 至今 | 从开关到平台策略 | GhostAction 第二波(2026-08–09)与第三波(2026-10)、执行策略 GA(2026-09-17)、锁文件预览→GA(2026-10-14/28)、公共仓库默认禁用 pull_request_target(2026-12-02 强制) |
1.1 阶段一:诞生与便利优先(2018–2019)
- 2018-10-16:Actions 进入有限公开测试,定位是"连接并分享容器来运行软件开发工作流",直接挑战 Travis CI / CircleCI / Jenkins 的传统 CI 市场。来源:GitHub Changelog: GitHub Actions (limited public beta)。
- 2019-11-13:GitHub Universe 2019 宣布 Actions 正式 GA,同期推出 1200+ 社区工作流生态与 Packages GA。来源:Universe 2019 Day One 官方博客。
- 2019-11 月初:自托管 runner(self-hosted runners)以 beta 形式推出,允许在自有基础设施上跑 workflow;同期引入内部仓库。来源:DevClass 当时报道、Changelog 归档。
- 2019 末–2020:
pull_request_target事件被引入,让维护者能在"目标仓库上下文"里跑 fork PR 的自动化(贴标签、评论、跑测试)。原始 changelog 存档本次未能检索到,引入时间按 GitHub 官方社区讨论表述为"2020 年前后"。来源:community discussion #179107 "Towards a secure by default GitHub Actions"。
驱动因素:微软收购 GitHub(2018-06)后需要快速补齐 CI/CD 短板,产品逻辑是增长优先——最宽松的默认值(GITHUB_TOKEN 读写、fork PR 事件灵活触发、公共仓库开放 self-hosted runner 绑定)都是为了让"五分钟上手"成立。安全在这个阶段是文档里的脚注。后来反复出事的三大隐患全部埋于此时——默认读写的 token、会把目标仓库 secrets 交给 fork 触发上下文的 pull_request_target、可被回溯改写的标签引用;外加一项同期埋下的生态风险:公共仓库可被外部 PR 绑定的 self-hosted runner(平台默认值问题)。
1.2 阶段二:第一波安全审视(2020–2021)
- 2020 年:
workflow_run事件推出,官方意图是提供"让不可信 PR workflow 触发可信 workflow"的替代模式。同年底 GitHub Security Lab 沉淀出"Pwn Request"分类——pull_request_target+ 显式 checkout 不可信 PR 头 = 以目标仓库写权限执行攻击者代码。来源:GitHub Security Lab: Preventing pwn requests。 - 2021-01 / 2021-02-23:Environments(环境)、环境保护规则与环境 secrets 先后进入 beta 和 API 发布,为"部署需要有审批门"提供载体。来源:community discussion #26262、Changelog 2021-02-23。
- 2021-04-20:推出组织/仓库级管理
GITHUB_TOKEN默认权限的设置。来源:Changelog: Control permissions for GITHUB_TOKEN。 - 2021-04-22:首次贡献者的 fork PR workflow 运行必须经有写权限的维护者批准——平台第一次把"审批"前置到执行链。来源:Changelog: Maintainers must approve first time contributor workflow runs、官方综述:GitHub Actions improvements for fork and pull request workflows。
- 2021-04:Codecov bash uploader 供应链事件。攻击者自 2021-01-31 起篡改 Codecov 的 CI 上传脚本约两个月,一行代码就把数千家客户 CI 环境变量里的 secrets 外传,HashiCorp、Twilio/QuickBooks 等下游遭殃。这是"CI 里跑的第三方脚本 = 供应链"的第一次大规模现实教学。来源:Codecov 官方安全更新、Rapid7 分析。
- 2021-08-16:workflow YAML 原生
permissions键进入 beta,可以把 token 权限缩到 job 级最小。来源:Changelog: native permissions for GITHUB_TOKEN in beta。 - 2021-10-27:OIDC 支持——workflow 可向云厂商出示自动生成的短生命周期 JWT,免存长期云 secrets。来源:Changelog: Secure cloud deployments with OpenID Connect、官方深度博客。
驱动因素:Codecov 事件是分水岭——它证明了"CI 是新的内网",直接催生 GitHub 官方《Security hardening for GitHub Actions》加固文档体系(见 docs: OpenID Connect 概念页 等)和 OIDC 落地。社区侧,OpenSSF Scorecard(2020 年推出)与 SLSA 框架(2021 年成型,此为模型既有知识,供上下文参考)也在推动"CI 供应链"成为独立议题。
1.3 阶段三:默认安全化与研究深化(2022–2024)
- 2022-10 → 2023-02-02:GitHub 宣布并落实
GITHUB_TOKEN默认权限从读/写改为只读(仅对新企业/组织/仓库生效,存量不变)。官方原话:旧的读/写默认"过于宽松"(too permissive)。这是权限模型的翻转时刻。来源:Changelog 2023-02-02(本次会话已直接抓取核验)。 - 2023–2024 年:缓存投毒(cache poisoning)被系统化公开——fork PR 的 workflow 虽只有只读 token,却与基础分支共享缓存,攻击者可投毒缓存让后续特权 workflow 中招;GitHub Security Lab 的加固系列(Part 4,2025-01-16)进一步指出连
permissions: {}都拦不住它——"看似无害的空权限配置可能意外为缓存投毒铺路"。来源:Adnan Khan: The Monsters in Your Build Cache(2024-05-06)、GitHub Security Lab 系列 Part 4。(原稿记"2023 年研究员 Adi Katz 公开该技术"并引用 Unit 42 链接,本次未能定位 Adi Katz 的原始发布,且该 Unit 42 链接实为 ArtiPACKED 研究——已按可核验来源改写,见文末不确定点⑨。) - 2024-01-11:John Stawinski 与 Adnan Khan 发表 《Playing with Fire》,真实复现对 PyTorch 的关键供应链攻击路径,把 AI 头部项目的 CI 风险具象化。来源:johnstawinski.com 原文。
- 2024-08-13:Palo Alto Networks Unit 42 发布 ArtiPACKED(作者 Adi Katz):
actions/upload-artifact上传过程存在竞态,.git/config、~/.docker/config.json等凭据文件会被打进 artifact,攻击者可赶在 workflow 结束前抢到有效GITHUB_TOKEN。来源:Unit 42: ArtiPACKED。 - 2024 年:Adnan Khan 相继发表缓存投毒深度研究(2024-05)与 Cacheract 概念验证(2024-12)——"cache-native malware"在构建缓存内自传播、把受害仓库变成新的传播节点,是 CI 领域蠕虫化传播的代表性研究。来源:The Monsters in Your Build Cache(2024-05-06)、Cacheract(2024-12-21)。SonarSource 同期完成不可信输入系列研究(来源)。(原稿此处记"GitHub Actions Worm 研究(fork PR 自传播 + TOCTOU 审批绕过)"并引用 Adnan Khan 2023-12 开场博文与 Horizon3 链接,本次核验:其站点 GitHub Actions 标签下并无 worm 专文、开场帖与"2024 年发表"不符、Horizon3 链接抓取 404——均按可核验来源改写,见文末不确定点⑨。)
- 同期生态工具成型:StepSecurity Harden-Runner(eBPF 运行时监控 + 出口过滤,后来正是它在 tj-actions 事件中率先检出攻击)。来源:StepSecurity 博客。
驱动因素:默认值终于翻转,靠的是"存量用户不会自己改设置"的平台责任逻辑(官方在 changelog 中明说旧默认太宽松);研究侧则进入学术化——pwn request、缓存投毒、artifact 竞态、缓存蠕虫,攻击面被系统测绘完毕,只差一次大爆发的引信。
1.4 阶段四:信任链危机(2025)
- 2025-03-14/15:tj-actions/changed-files 供应链攻击(CVE-2025-30066 / GHSA-mrrh-fwg8-r2c3)。攻击者劫持该流行 Action 后回溯性改写全部历史版本标签,payload 倾倒 runner 内存,使 workflow secrets 以明文进入构建日志;该 Action 当时被逾 2.3 万个仓库使用(StepSecurity 原话 "used in over 23,000 repositories",即广泛报道的"波及 2.3 万+ 仓库"的口径来源);StepSecurity Harden-Runner 于 3-14 检出,GitHub 3-15 下架。溯源指向另一 Action
reviewdog/action-setup的 PAT 泄露(CVE-2025-30154)——一次"供应链的供应链"攻击。来源:StepSecurity 检测报告(2025-03-14,本次会话已直接抓取核验)、Snyk 复盘、CISA 官方警报(2025-03-18,本次会话已直接抓取核验)、Wiz 对 reviewdog 入口的溯源(2025-03-17)。 - 2025-03 下旬起:GitHub 官方博客把"钉扎到完整长度 commit SHA"作为标准建议反复输出;后续上线仓库设置"要求 Action 必须钉扎到完整 SHA"(2025 年 8 月社区可见,但只约束远程 Action)。来源:GitHub Blog: 5 GitHub Actions every maintainer needs to know、community discussion #179107。Coder 团队公开复盘:正是因为坚持 SHA 钉扎,tj-actions 事件中他们的流水线自动拒绝执行被改写的代码(复盘讨论)。
- 2025-09-05:GhostAction 首波被 GitGuardian 披露:首轮确证 327 个用户、817 个仓库被注入恶意 workflow,外传 3325 个 secrets(npm/PyPI/DockerHub 凭据与 GitHub PAT,经明文 HTTP POST 外传)。攻击大量复用
pull_request_target类向量。来源:GitGuardian 分析、GitGuardian: GhostAction Returns(2026-10-07,含首波数字回溯,本次会话已直接抓取核验)。(原稿曾在此记"Wiz(2025-09-30)把目标范围扩大评估到 3 万+ 仓库、约 1900 个被控账号"——本次核验该归属无法成立:该数字实为 2026-10 第三波爆发,见阶段五与文末不确定点①。) - 2025 年:Immutable Releases(仓库级设置,发布后资产与 tag 不可再改)开始被采用,作为对"挪标签"攻击(正是 tj-actions 手法)的止血。来源:go-task/task 采用讨论。("普及"的证据面仍薄:目前公开可考的采用讨论限于个别仓库,故本稿改用"开始被采用"。)
驱动因素:tj-actions 的"标签可被回溯改写"击穿了 @v4 这类浮动引用的全部信任假设——今天审计过的代码不等于明天执行的代码。研究方给出的"全 SHA 钉扎"是正确但昂贵的手工答案:据 Wiz 研究的二手转引(原始 Wiz 页面本次未能定位,见文末不确定点②),全量 SHA 钉扎的仓库只占约 3.9%——用户侧修复采用率过低,直接铺垫了 2026 年的平台化方案。(GhostAction 随后于 2026-08–09 与 2026-10 两度复现并显著扩大,叙事见阶段五。)
1.5 阶段五:平台级"默认安全"重构(2026 至今,截至 2026-11-01)
- 2026-06-18 起:GitHub 在 Actions 设置中引入 Policies(策略)区,先以"控制谁/什么能触发 workflow"的 actor 规则进入预览(原始 changelog 存档本次未见,以后续 GA 页为准)。
- 2026-08-31 至 09-30:GhostAction 第二波。GitGuardian 报告又有 772 个仓库被注入恶意 workflow、瞄准 2577 个 secrets、波及 373 个用户/组织。来源:GitGuardian: GhostAction Returns(2026-10-07,10-13 更新;本次会话已直接抓取核验)、Aviatrix 威胁分析(2026-10-09)。
- 2026-09-17:Workflow execution protections GA——在组织/仓库层做"触发者白名单 + 触发事件白名单",支持按 workflow 文件定向、evaluate 影子模式、REST API 化。并宣布:对没有配置任何事件策略的公共仓库,默认规则将禁用
pull_request_target。来源:Changelog(本次会话已直接抓取核验)。 - 2026-10-08 至 09:GhostAction 第三波(迄今规模最大):约 1900 个被控账号向 30,000+ 仓库(涉及约 3000 个用户/组织)推送恶意 workflow,其中最终版 payload 在 10-08~09 两天内部署到 28,000 个新仓库。广泛流传的"3 万+ 仓库 / 约 1900 账号"即属于此——它是 2026 年 10 月的新爆发,而非 2025 年的存量评估,也非跨年"累计"数。来源:同上 GitGuardian 报告(10-13 更新,本次会话已直接抓取核验)。
- 2026-10-14 → 2026-10-28:Workflow dependency lockfiles(工作流依赖锁文件)从公共预览到 GA——把 workflow 引用的每个 Action(含传递依赖)锁到已验证 commit,
@v4这样的可读引用保留,但实际执行的是被锁的 commit,标签再被挪也无效;更新须过依赖审查。来源:Changelog 预览、Changelog GA、官方文档(本次会话已直接抓取核验)。GitHub 官方安全参考已将锁文件列为最高强度措施,称其对采纳方"supersedes SHA pinning"(Secure use reference)。 - 2026-10-21(编辑注):公共仓库
pull_request_target默认禁用的强制执行日期从 2026-11-02 推迟至 2026-12-02。来源:同上 2026-09-17 changelog 内编辑注(本次会话已核验)。截至本报告日期(2026-11-01),强制尚未发生,处于迁移窗口期。 - 2026-10 下旬:GitHub 官方博客《GitHub Actions Lockfiles and Safer Deployments》(2026-10-18)把锁文件与部署安全并列为平台级方案(标题与日期经搜索摘要确认,未逐字核验原文)。
驱动因素:八年教训收敛为一个判断——把安全决策留给两亿仓库的维护者是不现实的;pull_request_target 的 Pwn Request 类漏洞、标签挪用类供应链攻击都被证明"用户侧修复采用率过低"(GhostAction 三波复现是最新证据),必须由平台以默认规则 + 锁文件 + 执行策略三层兜底。
1.6 纵轴小结:驱动因素与参与者全景
- 便利与安全的原生张力:Actions 的每一步安全演进,几乎都是对诞生期默认值的追溯修正——默认读写的 token(2023 才翻转)、把 secrets 带进 fork 上下文的
pull_request_target(2026 才默认禁用)、可变标签引用(2026 才有锁文件);外加同期埋下的生态风险:公共仓库可绑定 self-hosted runner(官方至今的措辞仍是"几乎绝不")。新功能先跑,安全后付账。 - 事件驱动强于前瞻设计:Codecov(2021)→ 加固文档与 OIDC;ArtiPACKED(2024)→ artifact v4 与清理实践;tj-actions(2025)→ SHA 钉扎与锁文件。平台的每次大幅收紧,背后都站着一次公开的重大事故。
- 研究社区是隐性主角:GitHub Security Lab 的 Pwn Request 分类、Unit 42、SonarSource、Adnan Khan、StepSecurity 构成了从"发现一个漏洞"到"定义一类漏洞"的产业研究链路,官方默认策略大量建立在这些分类之上。
- 责任归属的三级跳:用户自己加固(2020–2022 文档化)→ 平台给更好用的开关(2021–2025 permissions 键、审批、OIDC、SHA 钉扎设置)→ 平台直接下发默认策略(2026 执行策略、锁文件、默认禁用)。
- Secrets 主题的演化路径:明文环境变量(2019)→ 环境级 secrets 与保护规则(2021)→ OIDC 短时凭据替代长期 secrets(2021-10)→ "日志即泄露面"(tj-actions 把 secrets 打进构建日志)→ 运行时出口监控(Harden-Runner)与平台级异常运行拦截(2026)。
二、横轴:横向对比(2026-11-01 时间截面)
把 GitHub Actions 放在 2026 年 11 月 1 日的 CI/CD 安全赛道上,与 GitLab CI/CD、CircleCI、Jenkins、Azure DevOps Pipelines、Bitbucket Pipelines、Buildkite 六家对比。竞品入选逻辑:同处 2026 年、面向同类工作负载、必须回答同一组安全问题(权限下发、身份联邦、Secrets 存放、不可信代码执行、制品完整性)的平台。
2.1 总览对比表(2026-11-01 截面)
| 维度 | GitHub Actions | GitLab CI/CD | CircleCI | Jenkins | Azure DevOps | Bitbucket Pipelines | Buildkite |
|---|---|---|---|---|---|---|---|
| 权限模型 | ★★★★★ permissions: 键可到 job 级,GITHUB_TOKEN 可强制默认只读 | ★★★☆ CI_JOB_TOKEN 作用域可配,细粒度靠 protected 分支/环境 | ★★★☆ contexts + restricted contexts | ★★☆ 全靠 RBAC 插件 | ★★★★ 构建服务账号 + 默认限权选项 | ★★☆ 仓库/工作区两级,粗粒度 | ★★★★ Teams RBAC + agent 权限在自己手里 |
| OIDC 短期凭证 | ★★★★★ 原生,id-token: write,联邦 AWS/GCP/Azure | ★★★★★ id_tokens 关键字 per-job JWT,原生 Vault/AWS | ★★★★★ V1/V2/V3 token,claims 粒度业界最细之一 | ★★☆ 无原生,靠 OIDC 插件 | ★★★☆ 服务连接支持 Workload Identity Federation | ★★★★ 官方 OIDC IdP,联邦 AWS/GCP/Vault | ★★★★ 官方首选策略即 OIDC 联邦 |
| Secrets 管理 | ★★★★ org/repo/environment 三级 + masking;无原生外部 vault | ★★★★★ 变量 protected/masked/hidden + 原生 Secrets Manager(OpenBao) | ★★★★ contexts 可跨项目共享与限制 | ★★★ Credentials Store + Binding 插件(历史 CVE) | ★★★★ variable groups + Azure Key Vault 直连 | ★★★ secured 变量,无 vault | ★★★★ 平台不存 secrets,hooks 在你自己的基础设施 |
| Fork PR 隔离(按"默认安全性"评定,见表注) | ★★★☆ 默认不下发 secrets(GITHUB_TOKEN 只读)+ 首次贡献者需审批,但 pull_request_target 是默认可用的高危逃生门 | ★★★★☆ fork MR 流水线在 fork 上下文运行,上游 protected 变量不可达 | ★★★★ 默认不向 fork 构建传 secrets,开启需 OIDC claim 校验 | ★★ 无内置 fork 概念,全靠自建 | ★★★★ fork 构建默认禁 secrets,可关(有 Defender 策略盯防) | ★★★★★ fork PR 根本不触发流水线,无开关 | ★★★★★ secrets 不出你的基础设施,fork 默认拿不到 |
| 供应链防护(签名/出处) | ★★★★★ attest-build-provenance + Sigstore,官方宣称可达 SLSA L3 | ★★★☆ Ultimate 内置扫描套件,provenance 需自建 cosign | ★★★ security orbs + config policies | ★★ 插件供应链是最大风险源 | ★★★ 与 Defender for Cloud 联动 | ★★ 提供 git-secrets-scan 工具 | ★★★ 无平台级签名,可自建 Sigstore |
| 第三方加固工具生态 | ★★★★★ zizmor、Harden-Runner、Scorecard、pinact… | ★★★ 内置扫描为主 | ★★ orbs 生态 | ★★ 插件市场(良莠不齐) | ★★ 扩展市场 | ★★ | ★★ |
| 近年重大安全事件 | tj-actions(2025-03)、GhostAction(2025-09 首波、2026-08–09 第二波、2026-10 第三波;详见纵轴·阶段四/阶段五) | 平台级事故较少 | 2023-01 全客户 secrets 泄露事件 | 常年插件 CVE 流 | 无标志性集中事件 | 无标志性集中事件 | 无标志性集中事件 |
| 成本模型(Linux 主力档) | $0.006/min(2026-01 起降价约 39–40%,新增 $0.002/min 平台费) | 按席位订阅(Premium $29/人/月,Ultimate 报价制,数字未核验) | credits 计费,Performance $15/月起(二手) | 软件免费,人力+基础设施成本最高 | 订阅 + 并行作业付费 | 按席位 + 构建分钟 | 按席位,计算自理 |
表内星级为本研究的定性评估,依据下文逐项来源,非任何第三方评分。各行衡量该维度的综合实现水平;唯一例外是 Fork PR 隔离行:按 2.5 的"默认安全性"口径评定(Bitbucket ≈ Buildkite > GitLab ≈ Azure > GitHub 默认态),"可用性 × 安全"的综合体验讨论见 2.5 与 2.9。
2.2 权限模型:GitHub 的 permissions: 键仍是行业最细粒度
GitHub Actions 的核心武器是 permissions: 键:可在 workflow 级和 job 级给 GITHUB_TOKEN 逐权限收窄,官方安全参考明确建议"将 GITHUB_TOKEN 的默认权限设为只读,再按 job 提权",并提供阻止"workflows 创建或批准 PR"的开关与环境 secrets 的 required reviewers 机制(GitHub Docs: Secure use reference,本次会话直接抓取核对)。对比之下:GitLab 没有等价的 per-job token 权限键,权限模型围绕"protected 分支/标签 + protected 变量 + protected 环境"展开;CircleCI 用 contexts 与 restricted contexts 把 secrets 访问限制到指定分支/安全组,但没有"每个 job 一份低权 token"的概念;Jenkins 最弱,核心只提供 matrix-based 授权,pipeline 级最小权限要装 Role-based Authorization 等插件自行拼装(第三方加固清单 2026);Azure DevOps 默认可勾选"Limit job authorization scope to current project"等开关把构建服务账号的可达范围压小(Microsoft Learn);Buildkite 本质上把权限问题外包给自托管 agent 的宿主,平台只做编排。
结论:以"声明式、可下沉到 job 的最小权限"为标准,GitHub Actions 仍是第一梯队;GitLab/Azure 靠环境与分支保护"曲线救国"居第二梯队;Jenkins 属需要大量插件与人工纪律的第三梯队。
2.3 OIDC 短期凭证:2026 年已是全行业标配,比拼点转向 claims 粒度
七家平台里六家已原生支持 OIDC 联邦,唯一例外是 Jenkins(靠社区插件,社区讨论)。这是 2023→2026 三年间最大的行业收敛。GitHub:声明 permissions: id-token: write 即可拿 OIDC token,与 AWS/GCP/Azure 联邦(Secure use reference)。GitLab:id_tokens 关键字按 job 生成 JWT,且 job 必须先声明至少一个 ID token 才能用 secrets: 从 Vault 拉取,把"拿凭证"与"取秘密"绑定在同一份身份声明上(GitLab Docs: Secrets)。CircleCI:token 注入为 $CIRCLE_OIDC_TOKEN(V1/V2/V3 三版并存,V3 仅 Cloud,有效期一小时),V3 的 sub claim 包含 checkout-source 与 config-source,云侧信任策略可校验"流水线配置本身来自哪个仓库哪个分支"(CircleCI Docs: OIDC tokens,本次会话直接抓取核对)。Bitbucket 官方提供 OIDC IdP 与 AWS/GCP/Vault 的联邦指引(Atlassian 文档);Buildkite 官方安全清单把 OIDC 联邦、让长期凭证根本不存在列为 secrets 策略第一优先(Security checklist);Azure DevOps 服务连接支持 Workload Identity Federation,但对非 Azure 云的联邦体验不如前述顺滑。
结论:GitHub/GitLab/CircleCI 三家并列第一梯队;差异已从"有没有"转移到"信任策略能在多细的维度上校验调用方"——CircleCI V3 的 config-source claim 与 GitHub 的 environment 绑定是两个代表。
2.4 Secrets 管理:GitLab 凭原生 Secrets Manager 反超,Buildkite 架构哲学不同
GitLab 在 2025–2026 年补齐了最短的一块板:基于 OpenBao 的原生 Secrets Manager,secrets 可按 job、分支、环境作用域收窄并已宣布 GA(GitLab Docs;GitLab 官方博客),加上变量的 protected/masked/hidden 三标志,成为七家中唯一"内置变量体系 + 内置 secrets 管理 + 外部 Vault 联动"三层齐备的平台(判定口径:这里指全部由平台官方原生提供;GitHub 的 marketplace vault action 是第三方集成,不计入本口径——若放宽为"存在任何 vault 集成途径",GitHub 亦可算作三层,但并非官方原生)。GitHub 提供 org/repo/environment 三级 secrets 与日志 masking,配套 secret scanning + push protection——公共仓库免费自动启用,组织私有/内部仓库则需 GitHub Secret Protection(原 Advanced Security 体系拆分后的付费加购)(官方文档,本次会话直接抓取核对);原生外部 vault 集成缺席,靠 marketplace 的 vault action 补位。CircleCI 的 contexts 支持跨项目共享与限制,但 2023-01 泄露事件暴露了"平台集中托管 secrets"的单点风险:攻击者通过员工笔记本上的 infostealer 偷到带 2FA 的 SSO 会话 cookie 进入内部系统后,要求全体客户立即轮换存储在 CircleCI 中的所有 secrets(官方事件报告)——这场事件是行业转向"OIDC 优先、少存 secrets"的直接推手之一。Jenkins 的 Credentials Store + Credentials Binding 插件负责存储与日志 masking,但插件体系本身有 CVE 历史(如 2023-04-12 官方安全通告涉及凭据绑定与权限检查缺失)。Azure DevOps 的 variable groups 可直连 Azure Key Vault。Buildkite 根本不提供平台托管 secrets 的默认路径:secrets 放在你自己基础设施的 hooks/vault 里,平台只见编排不见秘密——对"平台侧单点泄露"天然免疫,代价是运维责任 100% 在用户。
2.5 Fork PR 隔离:本课题的核心差异点,七家走出三种哲学
这是七个平台分歧最大、也最能体现设计哲学的维度:
- GitHub:"默认可用但收权 + 一个高危逃生门"。官方原文明确:"With the exception of
GITHUB_TOKEN, secrets are not passed to the runner when a workflow is triggered from a forked repository"(Events that trigger workflows;Using secrets in GitHub Actions),fork 触发的pull_request中 GITHUB_TOKEN 只读,首次外部贡献者的 workflow 默认需维护者批准(2021-04 起,见纵轴·阶段二)。但pull_request_target触发器在基础仓库的安全上下文运行、自带 secrets 与写权限,官方文档警告"Avoid using thepull_request_targetworkflow trigger if it's not necessary",并提供专文《Securely using pull_request_target》与workflow_run替代方案;官方对公共仓库使用自托管 runner 的措辞极其强硬:"should almost never be used for public repositories"。社区安全讨论将其列为头号误用源,强调"fork 的 PR 数据必须当作潜在恶意输入"(GitHub 社区 Harden 讨论帖,2025-07)。 - GitLab:"在 fork 上下文运行"。fork 的 MR 流水线使用 fork 项目自己的 CI/CD 配置、runner 与变量,上游项目的 protected 变量对其不可达;"让 fork 流水线跑到父项目"的 epic(#3278)多年搁浅,卡点正是安全模型(GitLab Docs: Merge request pipelines)。
- Bitbucket:"默认禁用,无开关"。官方文档明确"Pull requests from a forked repository don't trigger the pipeline",且没有任何官方开关能让 fork PR 流水线跑起来;社区 workaround 都是"先合并进主仓分支再跑 CI"(Reddit r/devops 讨论)。最安全,功能牺牲也最大。
- CircleCI:"默认不传,开了要自己验证身份":fork 构建默认不生成 OIDC token、不传 secrets;若开启"Pass secrets to builds from forked pull requests",官方要求在云侧信任策略中校验
checkout-source/config-sourceclaims(CircleCI Docs)。 - Azure DevOps:"默认禁 secrets,可关,有雷达":fork 构建默认无法访问 secrets,管理员可开启;Defender for Cloud 有专门策略项《Azure Pipelines should not have secrets available to fork builds》盯着这个开关(Microsoft Learn)。
- Jenkins:无 fork 概念,隔离完全靠自建(独立 agent 池、按 job 划分 credentials 域),等于把 GitHub 用平台能力解决的安全题留白给用户。
- Buildkite:平台不持有 secrets,fork PR 默认接触不到任何平台侧秘密(Buildkite Docs)。
结论:默认安全性 Bitbucket ≈ Buildkite > GitLab ≈ Azure > GitHub(默认态)——2.1 表 Fork PR 隔离行的星级即按此口径评定;但可用性 × 安全的综合体验 GitHub 与 GitLab 更好——GitHub 用"收权 + 审批 + 逃生门需自证"换灵活性,代价是 pull_request_target 成为行业级事故温床(如 2024 年 asyncapi-bot 事件,见 Wiz: Hardening GitHub Actions)。
2.6 依赖钉扎与供应链防护:GitHub 被"自己的规模"反噬,也建了最厚的墙
GitHub Actions 的第三方 Action 生态是七家中唯一把"复用别人写的 CI 组件"变成主流默认的平台,这既是它最大的效率优势,也是最大的攻击面。标志性事件(tj-actions、GhostAction 的完整叙事见纵轴·阶段四与阶段五)共同证明了:平台安全文档写得再好,生态里"用 tag 引用第三方 action"的存量习惯才是真实防线。
平台侧回应与对手对比:
- GitHub:官方安全参考已把工作流依赖锁文件列为最高强度措施("supersedes SHA pinning"),同时保留"钉到 commit SHA、验证 SHA 来自原仓库而非 fork、audit 源码"的阶梯建议(Secure use reference)。制品完整性方面,
actions/attest-build-provenance用 job 的 OIDC 身份向 Sigstore Fulcio 换取短生命周期证书,为任意制品生成可验证的 SLSA 出处证明,官方宣称可支撑到 SLSA Level 3(Artifact attestations 文档;action 仓库;GitHub 博客, 2024-12)——这是七家中唯一的"一键式、平台原生 provenance"。 - GitLab:Ultimate 层内置 SAST/DAST/依赖/容器扫描套件,是"扫描能力内建"的天花板;但制品出处证明没有 GitHub 式原生方案,需自行接 cosign/Sigstore。
- CircleCI:security orbs 与 config policies 提供管道级治理,无原生 provenance。
- Jenkins:供应链问题反在自身——插件生态常年是 CVE 高发区,"用插件加固平台"与"插件本身是攻击面"构成结构性矛盾(Jenkins 2023-04-12 通告)。
- Azure DevOps / Bitbucket / Buildkite:攻击面天然小(复用型第三方组件文化弱得多),防护停在"联动外部工具"层面:Azure 与 Defender for Cloud 联动,Bitbucket 提供 atlassian/git-secrets-scan,Buildkite 靠用户自建 Sigstore。
结论:按"供应链防护纵深"排序是 GitHub > GitLab > 其余。GitHub 的独特处境是:攻击最猖獗的平台,同时是防护基建最完善的平台——攻击规模恰恰因为它的生态规模(同类上的"Windows 桌面困境")。
2.7 第三方加固工具生态:只有 GitHub 长出了"围绕 CI 平台本身的安全工具品类"
2026 年截面,围绕 GitHub Actions 已形成独立工具品类,其他平台基本没有对应物:zizmor(William Woodruff 主导的 workflow 静态分析器,扫描凭据泄露、pwn-request、模板注入等模式,2024-10 发布,已成社区事实标准,zizmor.sh;zizmorcore/zizmor;Open Source Security 访谈, 2025-05);StepSecurity Harden-Runner(agent 级出口监控,tj-actions 事件中的关键检测手段,Wiz 分析);配套还有 OpenSSF Scorecard、pinact(自动 SHA 钉扎)、Dependabot 对 workflow 依赖的版本升级。其他平台:GitLab 把对应能力内建为产品,CircleCI 靠 orbs,Jenkins 靠插件市场——但"社区安全研究者针对 CI 配置本身做静态审计工具"这一现象只在 GitHub Actions 生态出现,原因直白:生态规模大到养得起。
2.8 成本:安全含义藏在计费模型里
- GitHub Actions:官方现行费率 Linux 2-core $0.006/min(arm64 $0.005、1-core $0.002)、Windows 2-core $0.010、macOS $0.062(官方计费文档,本次会话直接抓取核对)。2025-12-16 官方宣布 2026-01-01 起 hosted runner 降价约 39–40%,同时新增 $0.002/min 平台费,后者因波及自托管 runner 引发社区强烈反弹,据二手报道对自托管部分的收费已回撤(官方 changelog 全文未逐一核验)。
- GitLab:Premium $29/人/月(自 $19 上调)、Ultimate 转报价制(定价页本次抓取超时,数字来自搜索结果,未核验);安全扫描套件绑定 Ultimate 层,安全能力的现金门槛比 GitHub(Secret Protection 加购)与免费自建之间更陡。
- CircleCI:credits 计费,Performance 计划 $15/月起(二手数据)。
- Jenkins:软件免费,但"插件治理、agent 隔离、凭证轮换"全部折算为人力成本,总拥有成本通常最高。
- Azure DevOps / Bitbucket / Buildkite:订阅+用量模型;Buildkite 按席位收费且计算自理——把 secrets 与隔离的成本也转移给用户,既是安全卖点也是成本负担。
从安全视角看计费:"平台托管 secrets 越多、平台被攻破的爆炸半径越大"——CircleCI 2023 事件让全行业为其买单一周的 secrets 轮换;而 OIDC/自托管模式在成本表上"便宜"的只是表,安全责任计入了另一张表。
2.9 适用场景结论
| 团队画像 | 推荐 | 理由 |
|---|---|---|
| 开源项目、重度 fork 协作 | GitHub Actions / GitLab | 默认隔离 + 审批 + 生态工具(zizmor 等)最全;Bitbucket 最安全但 fork PR 无法触发 CI,功能代价大 |
| 合规重型、要审计与扫描套件 | GitLab Ultimate,或 GitHub Enterprise + Secret Protection + attestations | 前者扫描内建,后者供应链出处(SLSA L3)内建 |
| 极致控制、安全责任自担 | Buildkite | secrets 不出你的基础设施,平台被攻破不等于 secrets 被攻破 |
| 深度 Azure 生态 | Azure DevOps | Key Vault 联动 + Defender 策略盯防 fork 开关 |
| 已有 Jenkins 资产 | 继续用,但按"插件最小化 + OIDC 插件 + 独立 agent 域"加固 | 无原生能力,一切靠纪律 |
三、交叉洞察:两轴结合说明了什么
单看纵轴,这是一个"平台被事故推着走"的故事;单看横轴,这是一张"2026 年各家功能对照表"。两条轴交叉之后,才能看到 GitHub Actions 安全问题的真实结构,以及普通用户应该怎么判断和行动。
3.1 五条洞察
洞察一:GitHub 的安全短板从来不是"能力短板",而是"默认值与存量习惯的短板"——2026 年的翻转正在补上这一课。
横轴显示,GitHub 在权限粒度(唯一可下沉到 job 的 permissions: 键)、OIDC、供应链出处(唯一原生 SLSA L3 provenance)上都是第一梯队;纵轴则显示,这些能力发布到"默认生效"平均晚了三到五年——permissions 键 2021-08 就有 beta,默认只读 2023-02 才对新仓库生效;OIDC 2021-10 就有,但"全 SHA 钉扎的仓库只占约 3.9%"(Wiz 研究的二手转引,见文末不确定点②)说明存量习惯根本不跟随。能力与默认值的错位,正是 tj-actions、GhostAction 能持续得手的结构性原因。2026 年的执行策略、锁文件、pull_request_target 默认禁用(12-02 强制),本质上是平台承认"提供开关不等于解决问题",把安全决策从两亿仓库的维护者手里收回来。横轴的另一个佐证是:GitLab/Azure 的权限模型粒度不如 GitHub,但靠 protected 资源这类"硬默认边界",平台级标志性事故反而更少——谁能在 org/instance 层强制安全默认值,谁的存量事故少(当然,事故更少部分也因其生态规模更小,见洞察二)。
洞察二:攻击集中度与防护繁荣度是同一枚硬币的两面,都不是"最弱"的证据。
横轴的重大事件一栏里,GitHub Actions 独占两行(tj-actions、GhostAction 三波),Jenkins 是常年插件 CVE 流,其余四家几乎空白。表面看 GitHub 最不安全;但纵轴的阶段划分显示,这些攻击全部命中的是诞生期遗留的默认值与生态习惯(逃生门事件、tag 浮动引用),而非平台当前防护的失守——Coder 靠 SHA 钉扎自动躲过 tj-actions 就是反证。同时,只有 GitHub 生态养出了 zizmor、Harden-Runner、pinact 这样的第三方安全工具品类。这是"Windows 桌面困境"的 CI 版:攻击者追着规模最大的目标打,平台被攻击教会得也最快。对用户的推论是:不能因为事故新闻多就判定 GitHub Actions 不安全,也不能因为防护文档全就默认自己安全——事故集中在"没跟上默认值"的存量仓库里,你要确认自己不在其中。
洞察三:Codecov(纵轴转折点)与 CircleCI 2023(横轴转折点)共同把行业推向同一个终局:消灭长期 secrets,而不是保护它们。
纵轴的 Secrets 演化路径止于"OIDC 短时凭据替代长期 secrets";横轴则给出了 Secrets 存放位置的三极收敛:平台托管(便利但单点,CircleCI 2023 为证——一次员工笔记本沦陷逼全行业轮换 secrets)→ 自托管(Buildkite 模式,平台不存秘密但责任 100% 外包)→ 无长期凭证(OIDC 联邦,行业终局)。2021 年 Codecov 事件证明了"CI 环境变量里的秘密就是攻击者的提款机",2023 年 CircleCI 事件证明了"连平台集中托管也扛不住自身被攻破"。两次事件分别从"怎么存"和"存在哪"两个方向论证了同一个结论:唯一的正解是让秘密在 CI 里不存在。GitLab 2025–2026 补的原生 Secrets Manager 是对"必须托管"场景的补全,而所有平台的安全清单(Buildkite 尤其直白)都已把"OIDC 联邦、让长期凭证根本不存在"列为第一优先。
洞察四:pull_request_target 是纵轴与横轴的最佳交汇样本——横轴的"设计哲学差异"其实是在描述纵轴"事故史"的分布。
横轴发现七家 fork PR 隔离有三种哲学:GitHub"默认可用但收权+逃生门"、GitLab"在 fork 上下文运行"、Bitbucket"默认禁用";纵轴则完整记录了 GitHub 逃生门从便利(2019–2020 引入)到被研究定型(2020 Pwn Request 分类)到事故温床(PyTorch 攻击验证、缓存蠕虫 Cacheract、GhostAction 复用该向量)到被平台关停(2026-09 宣布默认禁用、12-02 强制)的全生命周期。两轴合起来看:GitHub 的逃生门贡献了该领域最多的接管事故,但也倒逼出最详尽的官方反模式文档与替代方案;GitLab 的 fork 上下文模型让同类问题根本不存在,代价是 fork 流水线能力多年搁浅;Bitbucket 用功能缺失换安全,在 2026 年显得过度保守。这提示一个通用判断框架:评估任何平台的 fork/不可信代码模型,不要只看默认态星数,要看"逃生门的宽度和逃生门上装的锁"。
洞察五:责任归属的三级跳是行业规律,不是 GitHub 特例;且它正在所有平台上重演。
纵轴总结出"用户自己加固 → 平台给开关 → 平台下发默认策略"的三级跳。放到横轴上检验:GitLab 把安全扫描内建为 Ultimate 套件(第二级到第三级之间)、Azure DevOps 用 Defender 策略直接盯防用户的安全开关(第三级)、CircleCI 在 2023 事件后强制全客户轮换 secrets 并转向 OIDC 优先(事件驱动式的第三级)、Jenkins 停留在第一级(一切靠纪律)。这说明三级跳不是 GitHub 的独有叙事,而是整个 CI/CD 行业在 2020–2026 年的普遍运动方向——平台正在吃掉用户的安全责任。对用户而言这意味着:选平台时应把"平台愿意替你强制什么默认值"作为比"平台能配置什么"更重要的指标。
3.2 对用户的判断与建议
综合两轴证据,按优先级给出可执行清单(针对使用 GitHub Actions 的团队;选型建议见横轴·适用场景表;每条的落地操作见 3.3):
- 【最高优先级·2026-12-02 前】盘点并迁移所有
pull_request_target工作流。平台默认禁用的强制日期临近(纵轴·阶段五);能改pull_request+workflow_run的改,确需保留的走执行策略白名单显式声明,并对照官方反模式文档自查"显式 checkout 不可信 PR 头"这类 Pwn Request 模式(Securely using pull_request_target、Pwn Request 分类)。盘点命令、强制后的具体后果与豁免方式见 3.3(1)(2)。 - 【供应链】第三方 Action 从 tag 钉扎升级到工作流锁文件。锁文件已 GA(2026-10-28),官方定位是"supersedes SHA pinning"——保留
@v4可读性同时锁死实际执行的 commit(启用方式与能力边界见 3.3(3));存量仓库暂时做不到锁文件的(原因见 3.3(3)),至少钉扎完整 commit SHA 并验证 SHA 来自原仓库而非 fork(锁文件文档、Secure use reference)。CISA 已就 tj-actions 事件发布官方警报,其缓解建议以"审计影响窗口、立即轮换 secrets、更新到修复版本"为主,并将 GitHub 官方加固文档列为参考(见纵轴·阶段四);对"锁文件优先"的平台层方案,尚无政府级背书,本报告不据此引申。 - 【权限】org/enterprise 级把
GITHUB_TOKEN默认权限设为只读,再按 job 用permissions:键提权。这是官方安全参考的明确建议;2023 年的默认翻转只覆盖新建仓库,存量仓库要手动改。同时开启"禁止 workflows 创建或批准 PR"。 - 【Secrets】用 OIDC 联邦替代长期云凭证,尽量减少平台托管 secrets 的数量。
permissions: id-token: write+ 云侧信任策略是零长期凭证的标准路径;部署类 secrets 用 environment + required reviewers 绑审批链。参考 CircleCI 2023 教训:你托管的每一个 secret 都是平台的爆炸半径。 - 【自托管 runner】不要把 self-hosted runner 绑定到公共仓库。官方措辞是"should almost never be used for public repositories"——fork PR 代码可在其上持久化沦陷。
- 【工具链】把 zizmor 纳入 CI 静态检查、用 OpenSSF Scorecard 自评、敏感流水线加 Harden-Runner 类出口监控。这三个工具本身就是 tj-actions 类攻击的检测面(Harden-Runner 正是 2025-03 的首发检出来源)。安装命令与接入写法见 3.3(4)。
- 【制品】发布渠道启用 Immutable Releases + artifact attestations。前者堵"挪标签"攻击,后者给你的制品生成可验证的 SLSA 出处。
- 【选型】若安全责任无法自担,参考横轴结论:重度 fork 协作选 GitHub/GitLab;合规重型选 GitLab Ultimate 或 GitHub Enterprise + Secret Protection;要"平台被攻破 ≠ secrets 被攻破"选 Buildkite;已有 Jenkins 资产则按"插件最小化 + OIDC 插件 + 独立 agent 域"加固,不要指望平台默认值兜底。
3.3 落地路径:每条建议怎么上手(2026-11-01 时点)
3.2 回答"做什么",本节回答"怎么做"——每条配盘点手段、启用方式与工具入口,并回答 2026-12-02 强制的后果与豁免。所引机制与命令均经本次会话直接抓取官方文档核验(标注见文内)。
(1)先盘点:知道自己暴露在哪
pull_request_target使用面,三选一或交叉验证:- GitHub 网页代码搜索:
org:<你的组织> path:.github/workflows pull_request_target; - 本地一键:
grep -rn "pull_request_target" .github/workflows/; - gh CLI:
gh search code 'path:.github/workflows pull_request_target' --owner <组织>(需已登录 gh)。
- GitHub 网页代码搜索:
- SHA 钉扎覆盖率粗盘(口径:workflow 中
uses:总数 vs 其中钉到 40 位完整 commit SHA 的数量):
total=$(grep -rhE 'uses:' .github/workflows | wc -l)
pinned=$(grep -rhE 'uses:[^@[:space:]]+@[0-9a-fA-F]{40}' .github/workflows | wc -l)
echo "$pinned / $total"(粗口径:只看本仓库 workflow 文件,不含可复用工作流内部的引用。)
- 12-02 强制影响预览:默认
pull_request_target规则当前以 evaluate(影子)模式运行,只记录不拦截;在执行策略的 Insights 里可提前看到"强制开始后哪些 workflow 运行会失败"(changelog 原话 "see which workflow runs will fail once enforcement begins",本次会话直接抓取核对)。
(2)2026-12-02 强制:后果与豁免
- 后果:对没有配置任何 Actions 事件策略的公共仓库,默认规则将禁用
pull_request_target——依赖该触发器的 workflow 运行将失败/被阻止。默认规则的适用对象是公共仓库(见纵轴·阶段五 changelog 原文);私有仓库不在此次默认强制范围内,但执行策略机制同样适用于私有仓库,可自行下发。 - 豁免:不是"关掉平台",而是在企业/组织/仓库级创建 Actions 事件策略,显式 allow
pull_request_target(设置页或 REST API 均可),并可用 workflow 文件定向只对特定文件(如 deploy.yml)放行——changelog 原话:"explicitly allow pull_request_target in an applicable Actions event policy if your workflows still depend on the trigger"(本次会话直接抓取核对)。 - 推荐动作序列:先在 evaluate 影子模式跑一至两周 → 看 Insights 影响清单 → 能改
pull_request+workflow_run的改(对照官方反模式文档)→ 确需保留的白名单放行并按 Pwn Request 模式自查 → 两者都做不了的,至少用 workflow 文件定向收窄范围并挂审批。
(3)工作流锁文件:启用方式、能力边界、为什么存量仓库"暂时做不到"
- 启用:锁文件是仓库中的
.github/workflows/actions.lock,由 GitHub CLI 扩展gh actions-lock生成与更新——官方明确该文件"should never be edited by hand"(永远不要手改);可以逐个工作流接入(onboard),也可以一次性把仓库全部工作流 onboard(官方概念文档,本次会话直接抓取核对;具体操作见其"Locking action dependencies in a repository's workflows" how-to)。 - 能力:把工作流引用的每个 Action(含传递依赖)锁到具体 commit SHA;记录源仓库身份(
owner_id/repo_id,运行时校验仓库未被调包);官方原话:"For repositories that adopt a lockfile, SHA pinning within workflow files is no longer necessary."——这就是"supersedes SHA pinning"的准确含义;已接入的工作流同时满足"必须钉扎完整 SHA"仓库策略的检查。 - 边界(它不管什么,要写清楚):① 不覆盖可复用工作流调用(
jobs.<id>.uses)——需手动按完整 SHA 引用,其自身依赖由其定义所在仓库的锁文件负责;② 只管 runner 上执行的 action 代码,不管输入参数、secrets、runner 镜像;③ 锁定的 commit 必须能从锁文件记录的分支/标签追溯——"只存在于 fork 的 commit、或已从该分支/标签不可达的 commit,会在运行时被拒绝"。 - 为什么存量仓库"暂时做不到":机制 2026-10-28 才 GA(官方文档示例版本仍为 v0.0.3),启用需要每个仓库安装 CLI 扩展、生成锁文件并逐工作流接入;使用可复用工作流的仓库还要先做一轮全 SHA 改造才能全覆盖。因此本报告的口径是:新仓库与已盘点仓库优先锁文件,其余仓库先钉扎完整 SHA、随后迁移——这正是 3.2 第 2 条"存量仓库暂时做不到锁文件的,至少钉 SHA"的完整含义。
- 更新:依赖变更后运行
gh actions-lock重新生成;版本更新可交给 Dependabot 管理(官方文档明示)。
(4)三个工具的上手路径
- zizmor(workflow 静态分析):安装
pipx install zizmor(或brew install zizmor、uvx zizmor、cargo install --locked zizmor、Dockerghcr.io/zizmorcore/zizmor);在仓库根目录运行zizmor .(也可扫单文件zizmor path/to/workflow.yml、扫远端仓库zizmor owner/repo@tag);CI 集成用官方 zizmor-action,--format=sarif可直连 GitHub code scanning(安装文档、用法文档,本次会话直接抓取核对)。 - StepSecurity Harden-Runner(运行时出口监控):在每个 job 的第一步加:
- name: Harden Runner
uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0
with:
egress-policy: audit先用 audit(只记录)跑出基线,再切 block 按自动生成的域名允许列表阻断未授权外传;注意全局黑名单在 audit 模式下也强制生效(仓库 README,本次会话直接抓取核对;tj-actions 正是由它于 2025-03 首发检出)。
- OpenSSF Scorecard(供应链自评):接入官方 action:
- uses: ossf/scorecard-action@<full commit SHA> # v2.6.0 or later
with:
results_file: results.sarif
results_format: sarif
publish_results: true注意 publish_results: true 要求 job 权限含 id-token: write;结果在仓库 Security 标签的 Code Scanning Alerts、scorecard.dev 徽章与 API 查看;无法用托管面板的私有仓库可按标准安装说明本地运行 scorecard CLI(ossf/scorecard-action README,本次会话直接抓取核对)。
(5)建议的落地次序(对应 3.2 优先级):盘点(pull_request_target 使用面 + SHA 覆盖率)→ 开 evaluate 影子模式看 12-02 影响清单 → 迁移或白名单 pull_request_target → 新仓库/重点仓库上锁文件,其余先钉 SHA → org 级默认只读 token → OIDC 联邦替代长期 secrets → zizmor 进 CI + 敏感流水线挂 Harden-Runner + Scorecard 季度自评。
四、参考来源
官方 Changelog / 博客(GitHub)
- Actions 有限公测 2018-10-16 | Universe 2019 Day One | 自托管 runner beta 2019-11-05
- GITHUB_TOKEN 控制权限 2021-04-20 | 首次贡献者审批 2021-04-22 | Fork PR 改进综述 | permissions 键 beta 2021-08-16
- OIDC 2021-10-27 | Passwordless deployments | 默认只读 2023-02-02
- Artifact Attestations 与 SLSA L3(2024-12) | 5 Actions every maintainer needs to know
- 执行策略 GA 2026-09-17 | 锁文件预览 2026-10-14 | 锁文件 GA 2026-10-28
官方文档(各平台)
- GitHub Docs:Secure use reference | Events that trigger workflows | Using secrets | Securely using pull_request_target | OIDC 概念 | GITHUB_TOKEN 权限控制 | 工作流依赖锁文件 | Artifact attestations | 执行治理 how-to | Actions 计费 | About secret scanning
- GitLab Docs:Secrets / id_tokens | Secrets Manager | Merge request pipelines | Secrets Manager GA 博客
- 其他平台:CircleCI: OIDC tokens | Microsoft Learn: Secure your Azure Pipelines | Atlassian: Bitbucket OIDC | Buildkite: Security checklist | Jenkins 2023-04-12 安全通告
事件、研究与工具
- Codecov 官方安全更新 | Rapid7: Codecov 分析 | CircleCI 2023-01 事件报告
- GHSA-mrrh-fwg8-r2c3 | StepSecurity: tj-actions 检测 | CISA 警报 2025-03-18 | Snyk 复盘 | Wiz: reviewdog 溯源 | Coder 复盘(SHA 钉扎规避)
- GitGuardian: GhostAction 分析(首波) | GitGuardian: GhostAction Returns(2026-10-07,含第二波与 10 月第三波数字) | Aviatrix: GhostAction 2026 分析
- GitHub Security Lab: Preventing pwn requests | GitHub Security Lab 系列 Part 4(含缓存投毒) | Unit 42: ArtiPACKED | Adnan Khan: The Monsters in Your Build Cache(2024-05) | Adnan Khan: Cacheract(2024-12) | SonarSource: untrusted inputs | John Stawinski: Playing with Fire
- Wiz: Hardening GitHub Actions | GitHub 社区 Harden 讨论帖(2025-07) | discussion #179107: Towards a secure by default GitHub Actions | discussion #26262: Environments beta | go-task/task: Immutable Releases 讨论 | DevClass: 自托管 runner 报道
- 工具:zizmor | zizmor 安装文档 | zizmor 用法文档 | zizmorcore/zizmor | Open Source Security 访谈(2025-05) | actions/attest-build-provenance | StepSecurity 博客 | step-security/harden-runner | ossf/scorecard-action
第三方博客与社区讨论(非官方来源)
- Reddit r/devops: Bitbucket fork PR 凭据泄露讨论 | Jenkins 加固清单 2026(tasrieit,第三方博客) | Jenkins OIDC 插件社区讨论
- 转引 Wiz"3.9% 全量 SHA 钉扎"统计的第三方博客(原始 Wiz 页面未能定位,见不确定点②):Arctiq: Top 10 GitHub Actions Security Pitfalls | Stéphane Robert: GitHub Actions 15 security pitfalls
不确定点与免责声明(承袭两章标注,修订后汇总如下)
- GhostAction 规模数字(本次修订已统一口径):原稿纵轴记"Wiz 2025-09-30 扩大评估 3 万+ 仓库 / 约 1900 账号"、横轴记"2026-10 累计扩散",两口径按字面互相矛盾。本次修订直接抓取 GitGuardian 2026-10-07 报告(10-13 更新)核实:3 万+ 仓库 / 约 1900 账号为 2026-10-08~09 新一波爆发(约 1900 个被控账号、目标 30,000+ 仓库),772 仓库 / 2,577 secrets / 373 用户为 2026-08-31~09-30 第二波,817 仓库 / 327 用户 / 3,325 secrets 为 2025-09 首波;原稿所引 Wiz 原文(ghostaction-campaign-github-workflows)本次抓取 404,"Wiz 2025-09-30 扩大评估"无法核验,已从正文移除。
- 全量 SHA 钉扎占比约 3.9%:多条第三方安全博客转引称为 Wiz 研究结果(Arctiq、Stéphane Robert 等,链接见上节),但原始 Wiz 页面本次未能定位(Wiz 2026-10-26 的 pwn request 研究中亦无此数),原始统计口径与样本未核验——按本报告标注方法,正文两处均标为"二手转引"。
- GitLab 定价(Premium $29/人/月、Ultimate 报价制):官方定价页抓取超时,数字来自搜索结果聚合,未核验。
- GitHub $0.002/min 平台费对自托管 runner"已回撤":来自二手报道与社区讨论,未逐条核对官方 changelog 原文;hosted runner 现行费率已由官方计费文档直接核验。
pull_request_target引入时间:原始 changelog 存档未检索到,按 GitHub 官方讨论表述为"2019 末–2020 年前后"。- 2026-06-18 Policies 预览与 2026-10-18 官方博客:仅经搜索摘要确认,未逐字抓取核验。
- OpenSSF Scorecard(2020)与 SLSA(2021)的时间点:模型既有知识,供上下文参考,非本次会话核验。
- 对比表星级:为本研究的定性评估,依据上文标注来源;其中 Fork PR 隔离行按"默认安全性"口径评定(与 2.5 结论一致),其余各行衡量综合实现水平,均非任何第三方基准测试。
- 本次修订中改写的引用:① 原稿"2023 年研究员 Adi Katz 公开缓存投毒"未能定位其原始发布(其署名的 Unit 42 ArtiPACKED 为另一研究,现仅用于 ArtiPACKED 本身),已改用可核验的 Adnan Khan 与 GitHub Security Lab 来源;② 原稿"GitHub Actions Worm 研究(2024)"归名 Adnan Khan 并引其 2023-12 开场帖、另引 Horizon3《PWN Request》——本次核验:adnanthekhan.com 的 GitHub Actions 标签下仅有 3 篇、无 worm 专文,Horizon3 链接抓取 404,已改用其已核验的缓存投毒与 Cacheract 研究(自传播/蠕虫化表述保留,原稿的 TOCTOU 审批绕过等未核验细节已删除);③ 原稿引用的 Wiz GhostAction 链接 404,首波叙事改以 GitGuardian 为准;④ reddit 链接原稿误标为"Atlassian 社区讨论",已改标 Reddit。
附录:审读意见处理记录(8 条)
| # | 审读意见 | 处理结果 |
|---|---|---|
| 1 | GhostAction 数字时间线自相矛盾(卷首①、阶段四 65–66 行、不确定点①) | 直接抓取 GitGuardian 2026-10-07 报告(10-13 更新)核实:3 万+ 仓库 / 约 1900 账号为 2026-10-08~09 第三波爆发,非 2025-09 存量、也非累计口径;"Wiz 2025-09-30 扩大评估"所引 Wiz 原文 404、无法核验,已移除。全稿统一为三波口径(2025-09 首波 / 2026-08~09 第二波 / 2026-10 第三波),卷首①、阶段四、阶段五、横轴事件行、不确定点①同步改写。 |
| 2 | 星级表与 2.5 结论冲突、表注未说明口径 | Fork PR 隔离行改按 2.5 的"默认安全性"口径评定:GitHub ★★★★☆→★★★☆、Buildkite ★★★→★★★★★,其余不变;表注写明"该行按默认安全性评定,其余各行衡量综合实现水平",并在 2.5 结论句中回指表注。 |
| 3 | "三大隐患"三处清单不一致 | 统一为"三大隐患(默认读写 token、pull_request_target 逃生门、可回溯改写的标签引用)+ 一项生态风险(公共仓库可绑定 self-hosted runner,平台默认值问题)";TL;DR、阶段一驱动因素、纵轴小结第 1 条三处同步。 |
| 4 | 引用错位与标签不符 | reddit 链接改标"Reddit r/devops 讨论"(正文与来源列表两处);Unit 42 链接不再同时支撑缓存投毒与 ArtiPACKED 两个研究;"Adi Katz 2023 缓存投毒"与"Adnan Khan Worm 2024"两处无法核验的引用按可核验来源改写(补 The Monsters in Your Build Cache 与 Cacheract 链接,删除 404 的 Horizon3 链接);tasrieit 与 reddit 移出"官方文档"小节,新增"第三方博客与社区讨论(非官方来源)"小节;全部记入不确定点⑨。 |
| 5 | 2026 年事件错放在 2025 年阶段 | GhostAction 第二波(2026-08-31~09-30)移入阶段五,并补入核验所得的第三波(2026-10-08~09);阶段总览表第 4/5 行、横轴"近年重大安全事件"行、2.6 交叉引用同步更新;阶段四驱动因素补跨章指引。 |
| 6 | 关键数字无出处、违反自定标注方法 | "2.3 万仓库"补 StepSecurity 原文口径("used in over 23,000 repositories",直接抓取核验)并在正文标注;"3.9%"改标"Wiz 研究的二手转引"并新增不确定点②,同时在参考来源新增两个转引来源链接。 |
| 7 | 结论性断言缺乏文本内支撑 | ① secret scanning 付费口径改为"公共仓库免费自动启用;组织私有/内部仓库需 GitHub Secret Protection 付费加购"并补官方文档链接(直接抓取核验);② "唯一三层齐备"补判定口径(官方原生集成 vs 第三方 marketplace action);③ Immutable Releases"普及"改"开始被采用"并注明证据面;④ "CISA 响应证明政府级共识"改为与警报内容匹配的表述(警报缓解建议为审计/轮换/更新,未背书锁文件),CISA 警报内容经直接抓取核验。 |
| 8 | 止步于"做什么"、缺"怎么做";规模混杂变量未承认 | 新增 3.3 落地路径:盘点命令(pull_request_target 使用面、SHA 覆盖率、evaluate 影子模式影响预览)、12-02 强制后果与豁免(事件策略白名单 + workflow 文件定向)、锁文件启用方式/能力边界/存量仓库暂时做不到的原因、zizmor/Harden-Runner/Scorecard 三工具上手路径与推荐落地次序;洞察一末尾补"(事故更少部分也因其生态规模更小,见洞察二)"。 |