12k
All articles

Аудит GitHub Workflows на предмет угроз безопасности

Проверьте GitHub Actions на права токена, script injection, риск pull_request_target, pinning действий, self-hosted runners и OIDC.

OpenReplay Team
OpenReplay Team
Аудит GitHub Workflows на предмет угроз безопасности

Атаки на цепочку поставок через GitHub Actions перешли из разряда теоретических в разряд рутинных. Компрометация tj-actions/changed-files в марте 2025 года наглядно показала, насколько быстро один заражённый action способен привести к утечке секретов из тысяч репозиториев. Год спустя история повторилась с инцидентом Trivy-action, когда злоумышленники принудительно перезаписали вредоносный код в 76 из 77 тегов версий.

Если у вас уже есть workflows, работающие в продакшене, этот чеклист поможет выявить наиболее распространённые уязвимости без полной переработки всего пайплайна.

Ключевые выводы

  • Устанавливайте permissions: {} на уровне workflow и предоставляйте каждому заданию только минимально необходимые права доступа.
  • Никогда не интерполируйте значения ${{ github.event.* }} напрямую в shell-команды — передавайте их через переменные окружения.
  • Закрепляйте сторонние actions за конкретными SHA коммитов и избегайте совместного использования pull_request_target с checkout кода из форков.
  • Замените статические облачные учётные данные на OIDC и запретите self-hosted runners выполнять недоверенный код в публичных репозиториях.

Прежде всего проверьте права GITHUB_TOKEN

Откройте любой файл workflow и найдите блок permissions верхнего уровня. Если он отсутствует, применяются настройки по умолчанию вашей организации — а организации, созданные до февраля 2023 года, нередко имеют права read-write по умолчанию.

Исправление достаточно простое:

permissions: {}  # запретить всё на уровне workflow

jobs:
  build:
    permissions:
      contents: read  # предоставить только то, что нужно заданию

Установка permissions: {} на уровне workflow вынуждает явно объявлять, что именно требуется каждому заданию. Задание, которое только читает код, никогда не должно располагать GITHUB_TOKEN с правами на запись в репозиторий.

Ищите недоверенные входные данные в shell-командах

Выполните поиск по файлам workflow — ищите ${{ github.event внутри блоков run:. Это наиболее распространённый паттерн инъекции в скрипты:

# Опасно: злоумышленник контролирует заголовок 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-скрипт, поэтому специальные символы оболочки во входных данных не смогут вырваться за пределы контекста и выполнить произвольные команды. Также обращайте внимание на записи в GITHUB_ENV и GITHUB_PATH в шагах, обрабатывающих пользовательский контент — злоумышленники могут использовать их для внедрения переменных окружения или вредоносных бинарных файлов в последующие шаги.

Проведите аудит использования pull_request_target

pull_request_target выполняется с доступом к секретам базовой ветки, что делает его полезным для workflows, которым необходимо оставлять комментарии к PR из форков. Риск представляет не сам триггер — опасность возникает при его сочетании с checkout кода из форка:

# Опасная комбинация
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. В конце 2025 года GitHub частично снизил этот риск, однако данный триггер по-прежнему остаётся высокорисковым при сочетании с checkout кода из форков.

Проверьте закрепление сторонних actions

Компрометация tj-actions/changed-files стала возможной, потому что команды ссылались на изменяемые теги вида @v35. Значения тегов могут быть незаметно перезаписаны. Закрепление за полным SHA коммита предотвращает подобные атаки:

# Уязвимо
- uses: tj-actions/changed-files@v35

# Безопасно
- uses: tj-actions/changed-files@d6babd6899969df1a11d14c368283ea4436bca78

Теперь GitHub предлагает политики на уровне организации для принудительного закрепления за SHA и блокировки workflows, использующих незакреплённые actions. Проверьте раздел Settings → Actions → General на уровне организации. Одного закрепления недостаточно — также рекомендуется выдерживать паузу в 7–14 дней перед переходом на новые версии actions, поскольку большинство компрометаций цепочки поставок обнаруживается в течение недели.

Оцените риски, связанные с self-hosted runners

Self-hosted runners по умолчанию являются постоянными. Скомпрометированный workflow может установить бэкдоры, которые сохранятся между заданиями. Ключевой вопрос — используют ли какие-либо публичные репозитории ваши self-hosted runners: если да, любой участник может отправить PR, который выполнит произвольный код на вашей инфраструктуре.

Проверьте группы runners в разделе Settings → Actions → Runners и убедитесь, что публичные репозитории из них исключены. Для чувствительных рабочих нагрузок отдавайте предпочтение just-in-time (JIT) runners, которые уничтожаются после выполнения каждого задания.

Замените долгосрочные облачные учётные данные на OIDC

Если ваши workflows аутентифицируются в AWS, Azure или GCP с использованием статических учётных данных, хранящихся в секретах, эти данные доступны каждому action и шагу в соответствующем задании. OpenID Connect (OIDC) устраняет эту проблему, выпуская краткосрочные токены, привязанные к конкретному заданию, непосредственно во время выполнения. Нечего похитить, ничего не нужно ротировать вручную.

Дополнительные проверки, заслуживающие внимания

  • Работа с артефактами: устанавливайте persist-credentials: false для actions/checkout, если только последующие шаги явно не требуют наличия токена. Это предотвращает сохранение токена checkout в локальной конфигурации Git для последующих шагов workflow.
  • Защита окружений: секреты для деплоя должны храниться в GitHub Environments с обязательным ревью, а не в виде обычных секретов репозитория.
  • Аттестация артефактов: для публикуемых пакетов функция аттестации артефактов GitHub обеспечивает верифицируемую связь между сборкой и исходным workflow.
  • OpenSSF Scorecards: Scorecards action можно настроить для автоматической проверки прав токенов, закрепления actions и инъекций в скрипты — это удобно для автоматического обнаружения регрессий.

С чего начать

Выполните поиск по директории .github/workflows/ по следующим паттернам: отсутствующие блоки permissions или установленные в write-all, вхождения ${{ внутри шагов run:, триггеры pull_request_target и ссылки на actions без полного SHA. Эти четыре проверки позволят выявить наиболее критичные проблемы в большинстве репозиториев без необходимости внедрять какой-либо новый инструментарий.

Заключение

Обеспечение безопасности GitHub Actions не требует полной переработки пайплайна — большинство реальных инцидентов восходит к небольшому набору повторяющихся ошибок: избыточные права токенов, небезопасная интерполяция недоверенных входных данных, изменяемые ссылки на actions и runners, доступные для недоверенного кода. Проработка описанных выше проверок даёт вам надёжную базовую конфигурацию. Дополните её автоматическим сканированием с помощью OpenSSF Scorecards или аналогичных инструментов — и вы будете обнаруживать регрессии до того, как они достигнут продакшена.

Часто задаваемые вопросы

Как найти все workflows в организации, использующие уязвимые паттерны?

GitHub поддерживает поиск по коду в масштабах всей организации. Ищите такие термины, как 'pull_request_target', 'permissions: write-all' или 'github.event.pull_request.title' с ограничением по пути path:.github/workflows. Для более глубокого анализа инструменты Octoscan, zizmor и OpenSSF Scorecards action могут сканировать репозитории целиком и автоматически формировать отчёты о правах токенов, незакреплённых actions и точках внедрения инъекций.

Означает ли закрепление actions за SHA, что я никогда не буду получать обновления безопасности?

Нет, но это делает обновления явными. Вы сами решаете, когда обновить SHA, предварительно изучив разницу между версиями. Такие инструменты, как Dependabot и Renovate, могут автоматически открывать pull requests с обновлёнными SHA, сочетая безопасность закрепления с минимальными затратами на обслуживание. Задержка в 7–14 дней перед слиянием этих обновлений дополнительно снижает риски, связанные со свежескомпрометированными релизами.

Всегда ли OIDC лучше, чем хранение облачных учётных данных в секретах?

Для AWS, Azure, GCP и большинства крупных провайдеров — да. Токены OIDC краткосрочны, привязаны к конкретному запуску workflow и не могут быть похищены для последующего использования. Настройка требует конфигурирования доверительных отношений у облачного провайдера, однако устраняет необходимость ротации и ограничивает радиус поражения в случае компрометации workflow. Статические секреты остаются допустимым запасным вариантом только тогда, когда OIDC не поддерживается.

В чём разница между pull_request и pull_request_target?

Триггер pull_request выполняется в контексте форка и не имеет доступа к секретам базового репозитория, что делает его безопасным для запуска тестов на недоверенном коде. Триггер pull_request_target выполняется в контексте базового репозитория с доступом к секретам и предназначен для таких задач, как простановка меток на PR. Именно сочетание pull_request_target с checkout кода из форка является той опасной комбинацией, которой следует избегать.

Open-source session replay

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.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.