FlawOpen

flawopen.com/GitHub Actions 安全/工作流脚本注入

GitHub Actions 如何发生脚本注入,怎样防范?

高权限下影响严重 CWE-78 CI/CD 工作流安全
通俗解释

工作流若在 Bash 启动前把 issue 标题或 pull request 字段插入脚本,就可能把数据变成 shell 语法。就像把姓名误当成指令的名牌打印机。通过环境变量传值,并在作为数据使用时给 shell 展开加引号,Bash 就会把值作为独立数据接收。实际影响还取决于令牌、机密、runner 和触发事件。

不可信上下文GitHub 事件字段,如标题、正文、分支名、标签和提交消息。视为外部用户可控字符串。
表达式替换GitHub 在 shell 启动前求值 ${{ ... }}。在 run: 中,输入可能被拼进脚本源码。
环境变量传递映射到 env: 会将值作为进程数据传递。给 shell 展开加引号,并避免 eval 或动态脚本。
权限边界事件和工作流决定令牌权限、机密及 runner 访问范围。影响受限于 job 可访问的资源。

根因:shell 解析前已插入数据

GitHub 在启动 run 步骤的 shell 前展开表达式。若不可信标题直接插入脚本,其字符可能改变 shell 程序,而非保留为数据。通过 env: 传值会使其与脚本文本分离;加引号后,在普通命令中仍作为参数。但这不能保护后续传给 eval、其他解释器或将参数当代码的工具。GitHub 将事件字段视为可能由攻击者控制的输入。

.github/workflows/triage.yml(不安全插值)不安全
# 不安全:事件数据会进入生成的 shell 脚本
name: Issue Triage
on:
  issues:
    types: [opened]

permissions:
  contents: read

jobs:
  triage:
    runs-on: ubuntu-latest
    steps:
      - name: Print issue title
        run: |
          printf 'Title: %s\n' "${{ github.event.issue.title }}"
.github/workflows/triage.yml(数据分离)已加固
# 更安全:通过环境变量传递事件值
name: Issue Triage
on:
  issues:
    types: [opened]

permissions:
  contents: read

jobs:
  triage:
    runs-on: ubuntu-latest
    steps:
      - name: Print issue title
        env:
          ISSUE_TITLE: ${{ github.event.issue.title }}
        run: |
          printf 'Title: %s\n' "$ISSUE_TITLE"

攻击路径概览

Step 1

用户控制事件字段

例如公开 issue 标题、pull request 标题或分支名称成为工作流输入。

Step 2

工作流将其拼入 shell 源码

run: 中直接使用的 ${{ github.event... }} 会在 Bash 解析脚本前求值。

Step 3

shell 解释被改变的程序

若该值改变 shell 语法,命令会以 job 进程身份运行。若值始终是加引号的参数数据,则不会形成这种注入。

Step 4

job 权限决定影响范围

被攻陷的 job 只能访问本次运行暴露的资源,包括有效 GITHUB_TOKEN 权限、提供的机密和 runner。仔细审查 issue 事件与 pull_request_target。

加固检查清单

一手来源

浏览相关安全主题与事故分析:完整安全目录 →
另请参阅 Cosign GHSA-fx35-mq7g-6g98:旧版 Bundle 验证绕过 Dependency Confusion:风险与防护 Docker 套接字暴露:风险与防护