Back

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

Аудит 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 или аналогичных инструментов — и вы будете обнаруживать регрессии до того, как они достигнут продакшена.

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

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

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

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

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

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.

OpenReplay