FlawOpen

flawopen.com/Безопасность GitHub Actions/Инъекция скриптов workflow

Как возникает инъекция скриптов GitHub Actions и как её предотвратить?

Высокий ущерб при повышенных правах CWE-78 Безопасность CI/CD workflow
Простыми словами

Workflow может превратить данные в синтаксис shell, если вставит заголовок issue или поле pull request в скрипт до запуска Bash. Это как принтер бейджей, который принимает имя за команду. Передавайте значение через переменную окружения и заключайте подстановку в кавычки. Тогда Bash получает её отдельно от скрипта. Последствия зависят от токена, секретов, runner и события.

Недоверенный контекстПоля событий GitHub: заголовки, тексты, имена веток, метки и сообщения коммитов. Считайте их строками под контролем пользователя.
Подстановка выраженияGitHub вычисляет ${{ ... }} до запуска shell. В run: ввод может попасть в исходный текст скрипта.
Передача через окружениеenv: передаёт значение как данные процесса. Кавычьте shell-подстановку, не используйте eval и динамические скрипты.
Граница привилегийСобытие и workflow задают права токена, доступные секреты и доступ runner. Последствия ограничены ресурсами job.

Причина: данные вставляются до разбора shell

GitHub подставляет выражения до запуска shell для шага run. Недоверенный заголовок в исходном тексте может изменить программу 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 либо именем ветки, которое принимает workflow.

Step 2

Workflow вставляет его в shell-код

Прямое выражение ${{ github.event... }} в run: вычисляется до разбора скрипта Bash.

Step 3

Shell разбирает изменённую программу

Если значение меняет синтаксис, команды выполняются с правами процесса job. В кавычках оно остаётся данными аргумента.

Step 4

Права job определяют последствия

Скомпрометированный job достигает только доступных ему ресурсов: прав GITHUB_TOKEN, секретов и runner. Проверяйте issue-события и pull_request_target.

Контрольный список защиты

Первичные источники

Другие темы безопасности и разборы:Полный каталог безопасности →
См. также Cosign GHSA-fx35-mq7g-6g98: обход проверки Dependency Confusion: риски и защита Доступ к сокету Docker: риски и защита