审计 GitHub Workflows 的安全风险
针对 GitHub Actions 的供应链攻击已从理论层面演变为常态。2025 年 3 月,tj-actions/changed-files 遭到入侵,清晰地展示了一个被污染的 Action 如何迅速在数千个代码仓库中泄露密钥。一年后,Trivy-action 事件再次重演了这一模式——攻击者通过强制推送恶意代码,篡改了 77 个版本标签中的 76 个。
如果您的生产环境中已有 Workflow 在运行,本检查清单可帮助您在无需重新设计整个流水线的前提下,识别出最常见的安全漏洞。
核心要点
- 在 Workflow 级别设置
permissions: {},并仅为每个 Job 授予所需的最小权限范围。 - 切勿将
${{ github.event.* }}的值直接插值到 Shell 命令中——应改为通过环境变量传递。 - 将第三方 Action 固定到完整的 Commit SHA,并避免将
pull_request_target与 Fork 代码的检出操作组合使用。 - 用 OIDC 替换静态云凭证,并限制自托管 Runner 在公共仓库中执行不受信任的代码。
首先检查 GITHUB_TOKEN 的权限
打开任意 Workflow 文件,查看顶层的 permissions 块。如果该块缺失,则将应用组织的默认配置——而 2023 年 2 月之前创建的组织,其默认权限通常为读写(read-write)。
修复方法非常直接:
permissions: {} # 在 Workflow 级别拒绝所有权限
jobs:
build:
permissions:
contents: read # 仅授予 Job 所需的权限
在 Workflow 级别设置 permissions: {} 会强制您明确声明每个 Job 的所需权限。一个仅用于读取代码的 Job,绝不应持有对仓库具有写入权限的 GITHUB_TOKEN。
排查 Shell 命令中的不受信任输入
在 Workflow 文件中搜索 run: 块内出现的 ${{ github.event。这是最常见的脚本注入模式:
# 存在风险:攻击者可控制 PR 标题
- run: echo "Checking ${{ github.event.pull_request.title }}"
安全的替代方案是通过中间环境变量传递不受信任的值:
- name: Check PR title
env:
TITLE: ${{ github.event.pull_request.title }}
run: echo "Checking $TITLE"
该值通过环境变量传递,而非直接插值到 Shell 脚本中,因此输入中的 Shell 元字符无法逃逸并执行命令。此外,还需留意在处理用户可控内容的步骤中对 GITHUB_ENV 和 GITHUB_PATH 的写入操作——攻击者可利用这些机制向后续步骤注入环境变量或恶意二进制文件。
审计 pull_request_target 的使用
pull_request_target 在可访问基础分支密钥的上下文中运行,这使其适用于需要对 Fork 发起的 PR 进行评论的 Workflow。风险并不在于触发器本身,而在于将其与 Fork 代码的检出操作组合使用:
# 危险组合
on: pull_request_target
jobs:
test:
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }} # 执行攻击者的代码
- run: npm test # 同时可访问您的密钥
如果您使用 pull_request_target,请确保没有任何步骤会执行来自 PR 分支的代码。GitHub 在 2025 年底对此进行了部分缓解,但当该触发器与 Fork 检出操作组合使用时,仍属高风险场景。
Discover how at OpenReplay.com.
审查第三方 Action 的版本固定
tj-actions/changed-files 的入侵事件之所以得逞,是因为团队引用了可变标签(如 @v35)。标签的指向可被悄无声息地篡改。固定到完整的 Commit SHA 可防止此类攻击:
# 存在漏洞
- uses: tj-actions/changed-files@v35
# 安全做法
- uses: tj-actions/changed-files@d6babd6899969df1a11d14c368283ea4436bca78
GitHub 现已提供组织级策略,可强制执行 SHA 固定,并使使用未固定 Action 的 Workflow 直接失败。请在组织级别的 Settings → Actions → General 中进行检查。仅靠固定 SHA 还不够——还应考虑在采用新 Action 版本前设置 7 至 14 天的冷却期,因为大多数供应链入侵事件会在一周内被发现。
评估自托管 Runner 的暴露风险
自托管 Runner 默认是持久化的。被攻陷的 Workflow 可以安装在 Job 之间持续存在的后门程序。关键问题在于:是否有公共仓库在使用您的自托管 Runner——如果是,任何贡献者都可以提交 PR,在您的基础设施上执行任意代码。
请在 Settings → Actions → Runners 下检查您的 Runner 组,并确认公共仓库已被排除在外。对于敏感工作负载,建议优先使用即时(JIT)Runner——这类 Runner 在每个 Job 结束后会被销毁。
用 OIDC 替换长期有效的云凭证
如果您的 Workflow 使用以密钥形式存储的静态凭证来向 AWS、Azure 或 GCP 进行身份验证,这些凭证将暴露给该 Job 中的每一个 Action 和步骤。OpenID Connect(OIDC) 通过在运行时签发短期、Job 范围的令牌来消除这一风险。没有可窃取的密钥,也无需手动轮换凭证。
其他值得执行的检查项
- 制品处理:除非下游步骤明确需要令牌,否则应在
actions/checkout上设置persist-credentials: false。这可防止检出令牌在本地 Git 配置中残留,从而被后续 Workflow 步骤访问。 - 环境保护:部署密钥应存放在配置了必要审批者的 GitHub Environments 中,而非作为普通的仓库密钥存储。
- 制品证明:对于已发布的软件包,GitHub 的制品证明功能可在构建产物与其源 Workflow 之间建立可验证的关联。
- OpenSSF Scorecards:Scorecards Action 可配置为自动检查令牌权限、固定的 Action 以及脚本注入问题,有助于自动捕获安全回归。
从哪里开始
在 .github/workflows/ 目录下搜索以下模式:缺失或设置为 write-all 的 permissions 块、run: 步骤中的 ${{、pull_request_target 触发器,以及未包含完整 SHA 的 Action 引用。这四项检查无需任何新工具,即可在大多数仓库中定位出风险最高的问题。
结语
保护 GitHub Actions 的安全并不需要对整个流水线进行彻底重构——现实中的大多数安全事件都可追溯到少数几类反复出现的错误:过于宽泛的令牌权限范围、对不受信任输入的不安全插值、可变的 Action 引用,以及暴露于不受信任代码的 Runner。逐项完成上述检查,即可建立起可靠的安全基线。在此基础上,结合 OpenSSF Scorecards 或类似工具进行自动化扫描,便能在安全回归问题进入生产环境之前将其拦截。
常见问题
GitHub 的代码搜索支持组织范围内的查询。可以搜索 'pull_request_target'、'permissions: write-all' 或 'github.event.pull_request.title' 等关键词,并将范围限定为 path:.github/workflows。如需更深入的分析,Octoscan、zizmor 以及 OpenSSF Scorecards Action 等工具可以扫描整个仓库,并自动报告令牌权限范围、未固定的 Action 以及注入点等问题。
不是的,但这会使更新变得显式可控。您可以在审查版本间差异后,自行决定何时更新 SHA。Dependabot 和 Renovate 等工具可以自动开启 Pull Request 来更新固定的 SHA,让您在享受固定 SHA 安全性的同时,无需承担繁重的维护工作。在合并这些更新前设置 7 至 14 天的延迟,可进一步降低受到新近被入侵版本影响的风险。
对于 AWS、Azure、GCP 以及大多数主流云服务商而言,答案是肯定的。OIDC 令牌具有短期有效性,其作用范围限定于特定的 Workflow 运行实例,且无法被窃取后用于后续攻击。配置过程需要在云服务商侧建立信任关系,但可以彻底消除凭证轮换的负担,并在 Workflow 遭到入侵时将影响范围降至最低。只有在不支持 OIDC 的情况下,静态密钥才是有效的备选方案。
pull_request 触发器在 Fork 的上下文中运行,无法访问基础仓库的密钥,因此适合用于对不受信任的代码运行测试。pull_request_target 触发器则在基础仓库的上下文中运行,可访问密钥,主要用于为 PR 添加标签等任务。需要避免的危险组合,是将 pull_request_target 与 Fork 代码的检出操作混合使用。
Gain control over your UX
See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.