Hotdry.

Article

Git 钩子与 CI/CD 审计日志驱动的供应链异常检测——Grafana Labs 安全事件复盘

通过 Grafana Labs GitHub Actions 安全事件,深入剖析 pull_request_target 触发器与分支名注入的攻击链,给出 Git 钩子加固、审计日志分析与 CI/CD 供应链异常检测的工程参数与实战配置。

2026-05-17security

2025 年 4 月 26 日,Grafana Labs 在其公开的 grafana/grafana 仓库中遭遇了一次供应链级别的安全事件:攻击者利用 GitHub Actions 工作流中 pull_request_target 触发器与分支名脚本注入的组合漏洞,在无需任何合并操作的情况下窃取了部署机器人的凭证,并通过该凭证推送了恶意工作流以图进一步横向扩展。整个攻击链在首次 CI 运行时就已达成初始访问,而 Grafana Labs 的检测系统在数小时内触发告警,响应团队随即冻结部署、轮换凭证并启动全量审计。本文将以此次真实事件为锚点,展开供应链异常检测在 Git 钩子层与 CI/CD 日志层的技术实现路径。

攻击链全解析:从分支名注入到凭证窃取

理解此次攻击的技术细节是设计防御方案的前提。攻击者并非直接入侵 Grafana Labs 的生产系统,而是利用了公开仓库中一个看似常规的 GitHub Actions 工作流作为跳板,其攻击链分为三个阶段。

第一阶段为初始访问,攻击者向 grafana/grafana 仓库提交了一个包含特殊构造分支名的 Pull Request。该分支名被设计为可突破工作流中对分支名的字面量处理 —— 当工作流中的 Node 脚本对分支名进行字符串拼接而不做转义时,攻击者可以通过反引号或 $() 等 shell 语法注入任意代码。由于 pr-patch-check-event.yml 使用了 pull_request_target 而非更安全的 pull_request 触发器,工作流运行时会自动将仓库级别的凭证(包括 GRAFANA_DELIVERY_BOT_APP_IDGRAFANA_DELIVERY_BOT_APP_PEM)注入到环境变量中,使攻击者在首个 CI 运行中即可获取这些高权限凭证。

第二阶段为凭证利用,攻击者利用获取到的 GitHub App 令牌(由被盗凭证生成)在同一仓库中创建了新分支 hrgqavynjp,并推送了一个恶意工作流文件。该工作流包含三个关键步骤:首先序列化所有可用的 GitHub Actions 密钥并写入文件;随后使用 AES-256-CBC 算法对这些密钥进行加密;最后用硬编码的 RSA 公钥加密 AES 密钥本身,并将两个加密产物作为构建制品上传。这一设计使攻击者无需直接外传明文凭证,而是将加密后的数据分块上传,极大降低了被简单网络监控捕获的可能性。

第三阶段为痕迹清理,攻击者在完成制品上传后删除了该恶意分支,试图抹去活动痕迹。然而,Grafana Labs 的日志分析系统通过 Loki 保留了完整的 GitHub 活动流,包括 grafana-delivery-bot 账户的分支创建、提交与删除行为,为后续的溯源分析提供了关键证据。

pull_request_target 触发器的风险边界

此次事件的核心风险点在于对 pull_request_target 触发器的误用。在 GitHub Actions 的事件模型中,pull_request 触发器在处理来自外部叉(fork)的 Pull Request 时,代码检出会切换到合并后的基础分支(base branch),且工作流以只读权限运行,不会自动注入 Secrets。而 pull_request_target 则是为维护者需要针对发起 PR 的叉仓库内容执行操作而设计的替代方案 —— 它会在叉来源的分支上运行工作流代码,并自动继承仓库级别的写入权限与完整 Secrets 访问权限。

这一设计初衷本身是合理的,但当工作流代码本身处理了任何来自 PR 的不可信输入(如分支名、PR 标题、评论内容)且未做充分转义时,攻击者即可借助该触发器在拥有写权限的环境中执行任意代码。安全研究者将此类漏洞命名为 Pwn Request,其本质是一个信任边界错误:工作流被赋予了超出其实际需要的权限级别,而这些权限最终被不可信来源的代码所利用。

对于大多数需要处理外部贡献的仓库,正确的做法是优先使用 pull_request 触发器,仅在确实需要写入权限或访问叉分支内容时才切换到 pull_request_target,并在后者的情况下严格限制 Secrets 的作用域或使用环境级别的密钥(Environment Secrets)配合强制审批流程来增加防护层级。

Git 钩子层面的前置防御

在 CI/CD 流水线之外,Git 钩子本身也可以作为供应链异常检测的第一道物理层。虽然此次攻击的触发点在 GitHub Actions,但组织内部在提交阶段引入的钩子检查同样可以减少类似风险的发生概率。

commit-msg 钩子可以用于验证提交信息中是否包含可疑的 shell 语法字符(如反引号、美元符号加括号等)。在预接收钩子中嵌入正则表达式检查,对提交信息中出现的 $\(\`&&||` 等高风险模式进行告警或拒绝:

#!/bin/bash
# .git/hooks/commit-msg
FORBIDDEN_PATTERNS='\$\(|`|`|&&|\|\|'
if echo "$1" | grep -Eq "$FORBIDDEN_PATTERNS"; then
  echo "Error: commit message contains suspicious shell syntax"
  exit 1
fi

pre-push 钩子则可以在本地验证即将推送的分支名是否符合安全策略。分支名注入攻击的一个前提是工作流对分支名做了字面量处理,攻击者依赖这种宽松的解析来绕过检测。在推送前对分支名进行语法检查,阻断包含换行符、控制字符或特殊 shell 元字符的分支名进入远程仓库,可以从根本上压缩攻击面:

#!/bin/bash
# .git/hooks/pre-push
BRANCH_NAME=$(git symbolic-ref --short HEAD)
if echo "$BRANCH_NAME" | grep -P '[^\x00-\x7F]'; then
  echo "Error: branch name contains non-ASCII characters"
  exit 1
fi

这些钩子应当作为仓库模板的一部分通过 git init templateDir 机制分发,确保所有开发者在克隆仓库时自动获得一致的防护。同时,钩子本身应纳入版本控制(排除可执行位),以便通过代码审查发现安全策略变更。

CI/CD 审计日志的采集与分析架构

Grafana Labs 在事件复盘阶段使用 Grafana Loki 作为核心日志分析平台,对 GitHub 活动流进行了多维度的回溯分析。其审计日志架构覆盖了供应链安全中最关键的六个维度:代码提交与悬空提交、GitHub Actions 触发与执行记录、Vault 中的凭证访问行为、认证尝试日志、容器镜像真实性校验,以及生产环境配置变更。

在 GitHub Actions 层面,Loki 通过 GitHub Webhook 将所有工作流运行事件(workflow_runworkflow_jobworkflow_dispatch 等)摄入并进行结构化解析。每条日志记录包含触发器类型、执行身份、运行持续时间、步骤序列以及制品操作摘要。通过在 Loki 中构建 workflow_runworkflow_job 的关联视图,安全团队可以快速识别异常的执行路径 —— 例如一个从未被合并的分支上的工作流在运行后创建了新分支,或者一个原本只读的工作流突然开始写入制品。

凭证访问日志的采集依赖于 Vault 的审计后端(Audit Backend)。Grafana Labs 将 Vault 配置为记录所有凭证访问请求,包括访问者身份、资源路径、访问时间与结果。将这些日志以结构化 JSON 格式推送到 Loki 后,可以通过以下 PromQL 风格的查询识别异常访问模式:

{service="vault", namespace="secrets"} 
  | json 
  | requester =~ "grafana-delivery-bot.*" 
  | latency > 2s 
  | count by (resource_path, requester) > 5

此查询用于发现部署机器人账户对同一密钥资源的高频异常访问,作为潜在凭证滥用的早期信号。

容器镜像真实性校验的日志则来源于镜像签名验证步骤。在 Grafana Labs 的构建流水线中,每个发布镜像都会在推送前经过 Sigstore Cosign 或 similar tooling 的签名。验证日志通过 container signing events 接入 Loki,安全团队可以通过比对待签名镜像的摘要与运行时拉取的镜像摘要,来确认未被篡改。事件期间,所有容器签名验证记录均被纳入 Loki 的审计视图,以确保无任何异常镜像进入生产集群。

TruffleHog、Gato-X 与 Zizmor:三类静态扫描工具的协同

事件后,Grafana Labs 宣布将 TruffleHog、Gato-X 与 Zizmor 三款开源工具整合到 CI/CD 流水线中,分别解决凭证泄露检测、工作流安全审计与静态分析三个层面的问题。

TruffleHog 是 credential 扫描领域的标准工具,能够在 git 历史、文件系统与环境变量中检测 300+ 种凭证模式。Grafana Labs 将其配置为每次推送时对所有仓库运行的强制 gate:任何提交如果触发 TruffleHog 告警,CI 将自动中止并拒绝合并请求。关键配置参数包括 --rule 参数用于定义自定义凭证正则表达式,以及 --no-update 参数用于禁止工具自动更新签名数据库以确保构建可重现性。在流水线中的典型集成方式如下:

- name: Scan for secrets
  uses: trufflesecurity/trufflehog@main
  with:
    path: ./
    base: ${{ github.event.repository.default_branch }}
    head: HEAD
    args: --rule=enterprise --no-update

Gato-X 是独立安全研究者 Adnan Khan 开发的专项工具,专注于识别 GitHub Actions 工作流中的不安全的配置模式。在 Grafana 事件中,正是该工具被部署用于发现所有使用 pull_request_target 且同时包含脚本注入风险的触发器组合。工具接受仓库根目录作为输入,输出包含不安全工作流的文件路径、触发器类型以及潜在攻击向量的详细报告。Grafana Labs 宣布将赞助该项目并将其集成到所有公开仓库的 CI 流水线强制检查中。

Zizmor 是 William Woodruff 开发的 GitHub Actions 工作流静态分析器,能够检测工作流配置层面的漏洞(如不当的权限声明、过宽的 concurrency 配置、以及 workflow_dispatch 触发器缺少输入验证等问题)。在 CI 中的集成方式为在构建前运行强制检查,任何高危告警均会导致流水线失败:

- name: Static analysis
  run: zizmor --format sarif .github/workflows/
  env:
    ZIZMOR_POLICY: HIGH
- name: Upload results
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: zizmor-results.sarif

三款工具的协同工作流覆盖了凭证层(TruffleHog)、工作流配置层(Zizmor)与供应链攻击面识别层(Gato-X),构成了纵深防御的静态检查环节。

金丝雀令牌与运行时监控

除了静态检查,Grafana Labs 还在事件响应中展示了金丝雀令牌(Canary Token)的实战价值。金丝雀令牌是一种诱饵凭证,将其部署在多个系统路径中(如环境变量、配置文件、Vault 中的非活跃路径等)。当攻击者尝试使用这些诱饵凭证时,安全团队会立即收到告警,从而在攻击早期阶段获得可见性。

在此次事件中,Grafana Labs 部署的金丝雀令牌在被尝试使用时触发了即时告警 —— 这一信号直接推动了响应团队在周末期间迅速集结并启动应急流程。金丝雀令牌的部署策略包括:在所有生产环境的非活跃凭证路径中散布;使用唯一的令牌格式以便溯源泄露源头;在 SIEM 中配置针对令牌使用失败的专门告警规则(排除正常的预期轮换行为)。

运行时监控层面,StepSecurity 的 Harden-Runner 被推荐用于所有 CI 运行器。该工具在 job 执行期间监控网络调用、文件写入与进程活动,当检测到异常行为(如大量密钥外传或出站流量到非预期主机)时会实时阻断并告警。对于公开仓库,Harden-Runner 提供免费的社区层级,对于企业级部署则提供更细粒度的策略控制。

分库密钥与最小权限令牌的工程参数

Grafana Labs 在事后修复中实施了凭证分库策略:将用于不同目的的密钥迁移到相互隔离的 Vault 路径,并使用各自的 IAM 角色绑定。关键的工程参数包括:每个 Vault 路径的最大密钥数量不超过 50 个;每个密钥的 TTL 设置不超过 24 小时;密钥轮换的自动化触发器覆盖 git push 事件与手动审批双重条件;所有生产密钥访问强制经过 GitHub Environment Secrets 机制并要求至少一名维护者审批。

对于 GitHub App 令牌,最小权限配置应遵循以下原则:内容读取权限仅授予确实需要读取源码的工作流;工作流管理权限仅在需要修改其他仓库工作流的场景中授予;管理权限应始终设置为 Read-only 以防止被攻击者利用来修改安全策略。权限审查应作为季度安全审计的标准项,并使用 GitHub GraphQL API 自动生成权限变更报告。

总结

Grafana Labs 此次供应链安全事件揭示了现代软件工程中一个关键但常被忽视的风险面:公开仓库中的 CI/CD 流水线本身就是一个可被利用的攻击向量。当 pull_request_target 触发器与分支名脚本注入结合时,攻击者可以在无需任何合并操作的前提下获取仓库级别的凭证并进一步扩展攻击范围。

有效的防御需要多层机制的协同:Git 钩子层在提交阶段阻断可疑语法进入远程仓库;静态扫描工具(TruffleHog、Gato-X、Zizmor)在 CI 中强制执行凭证安全与工作流配置合规;审计日志通过 Loki 实现供应链活动的全链路可观测;金丝雀令牌与运行时监控(Harden-Runner)提供攻击早期的可见性;凭证分库与最小权限令牌策略缩小单点泄露的爆炸半径。这些机制共同构成了一套可操作的供应链异常检测与响应体系,适用于任何依赖 GitHub Actions 或类似 CI 系统进行持续交付的组织。

资料来源:Grafana Labs 官方安全更新博客(2025 年 5 月 16 日);StepSecurity 事件分析报告。

security

内容声明:本文无广告投放、无付费植入。

如有事实性问题,欢迎发送勘误至 i@hotdrydog.com