关于 github-actions-hardening
面向 GitHub Actions 工作流作者和审查者的安全审计技能,聚焦触发器权限、表达式注入、令牌权限及 Action 固定版本等工作流特有风险。
主要功能
- 01判断触发器的信任级别与权限
- 02检查表达式导致的脚本注入
- 03审计令牌权限与机密处理
- 04检查 Action 引用及 SHA 固定
- 05按严重级别报告问题并给出修复
适用场景
- 01合并前审计现有工作流
- 02加固使用特权触发器的工作流
- 03检查令牌范围与机密处理
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ github/ awesome-copilot --skill github-actions-hardening安装后在 Claude Code 中输入/github-actions-hardening确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/injection.mdpermissions-and-tokens.mdreport-format.mdsupply-chain.mdtriggers-and-privilege.md详细介绍
GitHub Actions 安全加固
一个专注于 GitHub Actions 工作流的安全审查工具。它针对的是 Actions 特有 的威胁模型——即信任边界存在于触发器类型、令牌作用域和字符串插值之中——而不是通用安全扫描器所关注的应用程序代码漏洞。大多数工作流风险对语言层面的 linter 来说是不可见的,因为危险的代码就是 YAML 本身,以及 GitHub 在你的脚本运行之前将 ${{ }} 表达式展开进 shell 的方式。
何时使用本技能
当请求涉及以下内容时使用本技能:
- 审查、审计或加固
.github/workflows/下的任何文件 - 编写新工作流,并希望其默认就是安全的
- 使用了
pull_request_target、workflow_run或issue_comment触发器的工作流 - 关于
GITHUB_TOKEN权限或permissions:键的问题 - 将 action 固定到 commit SHA、tag 还是 branch
- 在
run:步骤中处理不受信任的输入(issue 标题、PR 正文、分支名、提交信息) - 从 Actions 进行 OIDC / 云认证,或 CI 中的密钥处理
- 公共仓库上的自托管 runner
- 任何类似"这个工作流安全吗?"、"加固我的 CI"或"审查这个 GitHub Action"的请求
核心洞察
在工作流中,${{ <expr> }} 会在 shell 执行脚本 之前 被 runner 展开进脚本中。 因此,像这样的步骤:
- run: echo "Title: ${{ github.event.issue.title }}"
并不是在传递一个变量——它是在 把攻击者控制的文本直接粘贴进你的 shell 命令。一个标题为 "; <attacker-command> # 的 issue 会被拼接进脚本并执行。这一单一机制是现实世界中最常见的 Actions 漏洞,而模型会经常性地生成它。把每一个包含外部贡献者可影响数据的
${{ }} 都视为代码注入的汇聚点。
执行工作流
对每个被审查的工作流,按顺序执行以下步骤。
步骤 1 — 梳理触发器与信任级别
阅读每一个 on: 触发器,并对工作流的权限进行分类:
push、pull_request(来自同一仓库)→ 以贡献者自身的信任级别运行- 来自 fork 的
pull_request→ 以 只读 令牌运行,无密钥(设计上安全) pull_request_target、workflow_run、issue_comment、issues→ 在 基础仓库 的上下文中运行,拥有 读/写令牌和对密钥的完全访问权限,但可以被 外部贡献者触发。这些是危险的触发器。
阅读 references/triggers-and-privilege.md 以获取完整的信任矩阵。
步骤 2 — 搜寻脚本注入
对每一个 run: 块、actions/github-script 中的每一个 script:,以及自定义 action 的每一个输入,列出所有 ${{ }} 表达式,并检查其中是否有任何表达式会解析为攻击者可控制的数据。高风险上下文包括:
github.event.issue.title、github.event.issue.bodygithub.event.pull_request.title、github.event.pull_request.body、.head.ref、.head.labelgithub.event.comment.body、github.event.review.bodygithub.event.pages.*.page_name、github.event.commits.*.message、github.event.head_commit.*github.head_ref以及 fork 作者可以设置的任何github.event.*字段
阅读 references/injection.md 以获取完整的汇聚点列表及安全模式修复方法。
步骤 3 — 检查特权触发器不会执行不受信任的代码
如果 pull_request_target 或 workflow_run 工作流检出 PR/fork 代码
(ref: ${{ github.event.pull_request.head.sha }})然后运行它(构建、测试、安装脚本、带生命周期脚本的 npm install 等),那就是针对特权令牌的远程代码执行。将其标记为 CRITICAL。安全模式是拆分为两个工作流:一个无特权的 pull_request 工作流运行不受信任的代码,另一个有特权的
workflow_run 工作流只消费其结果。
第 4 步 — 审计 permissions:
- 如果没有
permissions:块,工作流会继承仓库默认权限,这可能对所有内容都是读/写。标记它。 - 建议使用顶层
permissions: {}(全部拒绝)或contents: read,然后按 job 授予最小权限(例如仅在负责评论的 job 上使用pull-requests: write)。 - 标记任何
permissions: write-all或步骤实际并不需要的宽泛write权限范围。
阅读 references/permissions-and-tokens.md 了解按权限范围的指导以及 OIDC 设置。
第 5 步 — 审计 Action 引用(供应链)
对于每一个 uses::
- 第三方 action(不是
actions/*或github/*)必须固定到完整的 40 字符 commit SHA,而不是标签或分支。标签和分支是可变的;被攻陷的上游 action 可以把v1重写为使用你的 token 和 secret 运行的恶意代码。 - 第一方
actions/*风险较低,但固定 SHA 仍是强化后的推荐做法。 - 将
@main、@master或任何分支引用标记为 HIGH — 那是“latest”,随时可能在你不知情的情况下发生变化。 - 在行尾注释中注明人类可读的版本:
uses: foo/bar@<sha> # v2.1.0。
阅读 references/supply-chain.md 了解固定、用于 actions 的 Dependabot,以及 artifact/cache 风险。
第 6 步 — 检查 Secret 和输出处理
- 不得回显、打印或将 secret 写入日志;在涉及 secret 的步骤中不得使用
set -x/bash -x。 - 不得将 secret 传递给运行不受信任代码的步骤或不受信任的第三方 action。
- 写入
$GITHUB_ENV或$GITHUB_OUTPUT的不受信任多行数据可能注入环境变量或步骤输出 — 请使用随机分隔符 heredoc 形式,并且绝不写入原始用户输入。 actions/checkout默认会在磁盘上留下 token;当 job 之后运行不受信任代码时,请设置persist-credentials: false。
第 7 步 — 生成报告
使用 references/report-format.md 中的格式输出发现:首先是严重性汇总表,然后按问题类型分组的发现,包含文件、确切的违规 YAML、用简明英语说明的风险,以及具体的修改前后对比。绝不自动应用更改 — 将其呈现以供审查。
严重性指南
| 严重性 | 含义 | 示例 |
|---|---|---|
| 🔴 CRITICAL | 外部贡献者可触发的 token/secret 窃取或 RCE | pull_request_target 检出并运行 fork 代码;在特权触发器上的 run: 中使用 ${{ github.event.* }} |
| 🟠 HIGH | 可被利用的供应链或权限范围问题 | 使用可变标签/分支的第三方 action;write-all 权限;issue_comment 上的注入汇聚点 |
| 🟡 MEDIUM | 有条件或可链式利用的风险 | 缺少 permissions: 块;非 fork PR 作者可触及 secret |
| 🔵 LOW | 强化缺口,直接风险较低 | 第一方 action 未固定 SHA;在非特权 job 上保留 persist-credentials 默认值 |
| ⚪ INFO | 观察项,而非漏洞 | 固定的 SHA 旁边缺少版本注释 |
输出规则
- 始终先展示发现汇总表(按严重性计数)。
- 按问题类型分组,而不是按文件分组。
- 务必精确 — 引用违规行并给出行位置。
- 始终为每个 CRITICAL/HIGH 配上一段具体的修正 YAML 片段。
- 绝不仅因为 fork 的
pull_request运行不受信任代码就声称它危险 — 它没有 secret,并且使用只读 token。请将 CRITICAL 保留给特权触发器。 - 如果工作流已经强化,请说明,并列出已检查的内容。
参考文件
按需加载以下文件:
references/triggers-and-privilege.md— 每个触发器的信任矩阵,为什么pull_request_target和workflow_run具有特权,以及双工作流安全模式。- 搜索模式:
pull_request_target、workflow_run、issue_comment、fork、secrets、read-only token、trust boundary
- 搜索模式:
references/injection.md— 攻击者可控的${{ }}上下文完整列表,以及针对每个 汇聚点(run、github-script、action 输入)的env:变量安全模式。- 搜索模式:
script injection、github.event、head_ref、issue title、env、intermediate variable、actions/github-script
- 搜索模式:
references/permissions-and-tokens.md—GITHUB_TOKEN作用域、按作业类型划分的最小权限permissions:配方,以及使用 OIDC 进行云认证而非长期有效的密钥。- 搜索模式:
permissions、GITHUB_TOKEN、write-all、contents: read、id-token、OIDC、least privilege
- 搜索模式:
references/supply-chain.md— 对第三方 action 进行 SHA 固定、为github-actions配置 Dependabot、 跨workflow_run的产物与缓存投毒,以及自托管 runner 的暴露风险。- 搜索模式:
SHA pin、uses、mutable tag、Dependabot、download-artifact、cache、self-hosted runner
- 搜索模式:
references/report-format.md— 输出模板:摘要表格、发现卡片,以及修复前后的 代码块。- 搜索模式:
report、format、finding、summary、remediation、before、after
- 搜索模式: