Понедельник, 9:15 утра. CloudTrail показывает 47 вызовов
sts:GetCallerIdentity от IAM-ключа, который по документам принадлежит сервисному аккаунту Jenkins. Вызовы идут из IP-диапазона, не принадлежащего нашей инфраструктуре. Через 20 минут - алерт GuardDuty: тот же ключ запускает EC2-инстансы в регионе ap-southeast-1, где у нас нет ни одного ресурса. Источник утечки нашли через час: AWS_SECRET_ACCESS_KEY выводился в консольный лог Jenkins-джобы через env | sort в отладочном шаге, который разработчик добавил «на пять минут» три недели назад. По данным GitGuardian State of Secrets Sprawl Report 2024, за год на публичном GitHub появилось 12,8 миллионов новых секретов - и значительная часть скомпрометированных сред приходится не на рабочие станции, а на CI/CD-раннеры.Как атакующие крадут секреты из CI/CD-пайплайна
CI/CD-пайплайн - система с максимальными привилегиями: она деплоит в продакшн, пушит образы в реестры, управляет инфраструктурой. Скомпрометированный CI/CD-секрет - не абстрактная «утечка данных», а прямой доступ к production-среде. По данным IBM Cost of a Data Breach Report 2023, средняя стоимость инцидента - $4,45 млн. Для российских компаний с 2024 года добавляются оборотные штрафы за утечку персональных данных - до 3% годовой выручки. Мотивация считать секреты в CI/CD - вполне денежная. Подробнее - в нашем обзоре атаки на цепочку поставок.Полная цепочка атаки по матрице MITRE ATT&CK выглядит так: Reconnaissance (T1593.003, Code Repositories - сканирование публичных репозиториев) → Credential Access (T1552.001, Credentials In Files - извлечение из конфигурации; T1552.004, Private Keys) → Initial Access (T1078, Valid Accounts - утёкшие credentials используются как легитимные) → Impact (криптомайнинг, эксфильтрация данных, ransomware). На каждом этапе можно ловить - и на каждом обычно не ловят.
GitHub и GitLab: hardcoded secrets и poisoned pipelines
Боты прочёсывают публичный GitHub непрерывно. От появления секрета в коде до первой попытки его использования проходят минуты. Не часы - минуты. Если атакующий получил доступ к приватному репозиторию и вытаскивает секреты - это T1213.003 (Code Repositories, Collection).Типовые точки утечки API-ключей через GitHub:
- Hardcoded secrets в YAML-конфигурации - ключ в
env-блоке.github/workflows/deploy.yml. По данным GitGuardian, приватные репозитории содержат hardcoded-секреты значительно чаще, чем публичные. Логика «раз приватный - можно не прятать» убивает pull_request_targetв GitHub Actions - event имеет доступ к секретам репозитория, и если workflow чекаутит код автора PR - прямой путь к эксфильтрации- Docker-слои -
COPY .env /app/.envс последующимRUN rm /app/.envбесполезно: секрет остаётся в промежуточном слое образа. Удалил файл - а слой-то уже собран - Org-level секреты с чрезмерным scope - один org-secret доступен всем репозиториям. Компрометация одного репо = утечка всех org-секретов
Jenkins: утечка через build logs и credentials store
Русскоязычные материалы по secrets sprawl почти не покрывают Jenkins, хотя именно он остаётся одной из самых опасных поверхностей для утечки токенов. На практике - чаще всего именно через Jenkins и утекает.Build logs - главная проблема. Jenkins по умолчанию маскирует только credentials, явно переданные через
withCredentials {}. Всё остальное - printenv, set -x в shell-скриптах, verbose-режим Terraform (TF_LOG=DEBUG) - выводит секреты в консольный лог открытым текстом. Лог доступен всем с правами Job/Read, а на практике часто и анонимно, если Jenkins стоит за VPN без дополнительной аутентификации. Я видел Jenkins-инсталляции, где build-логи были доступны вообще без логина - через прямую ссылку.Credentials store хранит секреты на мастер-ноде в
credentials.xml. При дефолтной конфигурации ключ шифрования (master.key и hudson.util.Secret) лежит в $JENKINS_HOME/secrets/. Получив доступ к файловой системе мастера - через SSRF, RCE в плагине или скомпрометированный агент - атакующий расшифровывает все credentials. Это T1555.006 (Cloud Secrets Management Stores, Credential Access), применённый к on-premise системе.Master-agent коммуникация - ещё один вектор. Jenkins-агенты подключаются к мастеру по JNLP или SSH. Скомпрометированный агент может запрашивать credentials с мастера через внутренний API. Ограничить это можно через
Slave → Master Access Control, который по умолчанию не настроен. По умолчанию - это ключевое слово для всего Jenkins hardening.[Применимо: внутренний пентест, legacy Jenkins 2.x без hardening]
Сканирование секретов в репозиториях: Gitleaks vs TruffleHog
Два основных open-source инструмента для обнаружения hardcoded secrets в коде - Gitleaks и TruffleHog. Оба бесплатны и интегрируются в CI, но архитектурно работают по-разному.Требования к окружению:
- ОС: Linux, macOS, Windows
- RAM: 2 ГБ минимум, 4 ГБ для репозиториев с историей свыше 10 000 коммитов
- Git 2.x+, Docker опционально (для TruffleHog через
trufflesecurity/trufflehog) - Сеть: TruffleHog с верификацией требует интернет; Gitleaks работает полностью offline
| Критерий | Gitleaks | TruffleHog |
|---|---|---|
| Метод детекции | Regex-паттерны + entropy (опция) | Regex + entropy + верификация через API провайдера |
| False positives | Средний уровень | Низкий (верификация отсекает невалидные) |
| Скорость на 50k коммитов | Секунды | Минуты (API-вызовы) |
| Offline-работа | Полная | Частичная (без верификации) |
| Кастомные правила | .gitleaks.toml | Regex через конфиг |
| Когда использовать | Pre-commit хуки, быстрый CI-check | CI pipeline с верификацией, полный аудит |
| Когда НЕ использовать | Аудит с подтверждением валидности ключей | Pre-commit (слишком медленный) |
В боевых условиях работает связка: Gitleaks в pre-commit (быстрый, offline, мгновенная обратная связь), TruffleHog в CI-пайплайне (медленнее, зато верифицирует - шума на порядок меньше).
Три слоя secret scanning в DevSecOps-пайплайне
Слой 1: Pre-commit - блокировка до попадания в git-историю. Хук с Gitleaks останавливает коммит при обнаружении паттерна секрета:
YAML:
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.21.2
hooks:
- id: gitleaks
pre-commit install каждый git commit проходит через Gitleaks. Ограничение: хук работает только на машине разработчика и обходится через --no-verify. Это первая линия, а не замена CI-сканированию. Разработчик, который торопится в пятницу вечером, --no-verify напечатает быстрее, чем вы думаете.Слой 2: CI-пайплайн - сканирование в Pull Request. TruffleHog или Gitleaks запускается как шаг CI при каждом PR. В GitHub Actions -
trufflehog git file://. --since-commit=$BASE_SHA --fail. В GitLab CI - gitleaks detect --log-opts="$CI_MERGE_REQUEST_DIFF_BASE_SHA..$CI_COMMIT_SHA". Сканируются только новые коммиты. Нюанс: actions/checkout@v4 нужно вызывать с fetch-depth: 0, иначе при shallow clone --since-commit не сработает - и вы будете думать, что всё чисто, а на деле сканер просто не видит историю.Слой 3: Continuous scanning - полная git-история по расписанию. Команда
trufflehog git file://./repo --only-verified в scheduled pipeline ловит секреты, закоммиченные до внедрения сканирования. По данным GitGuardian, значительная доля исторических секретов остаётся валидной годами - и именно они самые опасные, потому что про них все забыли.Remediation: удаление секретов из git history и ротация ключей доступа
Обнаружить секрет - половина работы. Удалить его из git history и провести ротацию под давлением инцидента - вторая, и более болезненная.Удаление файла через
git rm и новый коммит не удаляет секрет из истории - он остаётся доступен через git log --all --full-history. Два инструмента решают задачу: BFG Repo-Cleaner (требуется bare-clone: git clone --mirror <url> repo.git, затем bfg --replace-text passwords.txt repo.git) и git filter-repo (git filter-repo --replace-text expressions.txt с поддержкой regex). BFG быстрее и проще для типовых кейсов. git filter-repo даёт больше контроля - когда секрет встречается в разных форматах. После очистки обязателен git reflog expire --expire=now --all && git gc --prune=now --aggressive.Ограничения обоих инструментов - и их нужно понимать до того, как начнёте:
- Переписывают историю - все хеши коммитов меняются, открытые PR ломаются, нужен force push
- Форки сохраняют старую историю с секретом - необходимо уведомить владельцев форков
- GitHub кеширует коммиты по SHA: даже после force push старый коммит доступен по прямой ссылке до обращения в GitHub Support
- Немедленная ревокация - отозвать ключ через консоль провайдера (AWS IAM, GCP IAM, GitLab PAT). Не через час - сейчас
- Генерация нового ключа с минимальными привилегиями, scope строго под одну задачу
- Обновление всех consumer'ов - CI/CD variables, Vault, Kubernetes Secrets
- Аудит использования - CloudTrail, GCP Audit Logs, audit log GitLab/GitHub: что делали с ключом между утечкой и ревокацией
- Очистка git history через BFG или
git filter-repo - Postmortem - как секрет попал в историю, почему не сработал pre-commit
Мониторинг утечек секретов GitHub Actions и Jenkins в SIEM
Сканирование репозиториев - превентивная мера. SOC нужны правила для обнаружения последствий утечки: использования скомпрометированных credentials.Sigma-правила и корреляция для SOC
В репозитории SigmaHQ есть правила, покрывающие TTPs утечки ключей доступа в pipeline:proc_creation_lnx_susp_git_clone.ymlиproc_creation_win_git_susp_clone.yml(T1593.003) - подозрительное клонирование репозиториев на хостах, где Git не штатный инструментgithub_outside_collaborator_detected.yml(T1213.003) - добавление внешнего коллаборатора в приватный репозиторийgithub_self_hosted_runner_changes_detected.yml(T1213.003) - изменения конфигурации self-hosted runner'ов, потенциальная подготовка к эксфильтрацииlnx_auditd_find_cred_in_files.yml(T1552.001) - поиск credentials в файлах на Linux-хостах (grep по паттернам password, secret, key)
- Аномальный доступ к credentials - запрос через Jenkins API (
/credentials/store/) с IP вне whitelist build-агентов - Массовое чтение build logs - один пользователь читает логи более 20 джоб за час (паттерн автоматического сбора секретов из логов)
- Модификация pipeline + немедленный build - изменение Jenkinsfile или
.gitlab-ci.ymlс запуском сборки в течение минуты (паттерн Poisoned Pipeline Execution)
repo.download_zip или git.clone для приватного репозитория от IP вне корпоративного диапазона; создание Personal Access Token с scope repo и немедленный API-вызов к /repos/{org}/{repo}/contents/; изменение visibility репозитория с private на public.Ключевая связка - CI/CD audit log + CloudTrail/GCP Audit Logs: если IAM-ключ, используемый в CI/CD, вызывает API из нового региона или IP - baseline-аномалия. Правило: ключ сервисного аккаунта Jenkins →
sts:AssumeRole или iam:CreateAccessKey из нетипичного источника → алерт Critical. Если у вас этого правила нет - заведите сегодня.Чеклист: предотвращение утечки credentials в CI/CD
📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
Большинство команд, с которыми я работал, внедряют аудит секретов в исходном коде «слой за слоем»: сначала CI-gate (самый высокий ROI - ловит всё до мерджа), потом pre-commit (снижает нагрузку на CI), потом scheduled scan (закрывает исторический долг). Jenkins hardening часто откладывают - и зря: именно Jenkins-логи оказываются источником утечки чаще, чем git-коммиты, в организациях с приватными репозиториями.
Привычка полагаться на один инструмент - типичная ошибка. Gitleaks в pre-commit не заменяет TruffleHog в CI, а оба не заменяют мониторинг использования скомпрометированных credentials в SIEM. На практике я видел организации с идеально настроенным secret scanning, которые месяцами не замечали, что утёкший три года назад ключ всё ещё валиден и используется из Юго-Восточной Азии - потому что никто не коррелировал CloudTrail с инвентарём CI/CD-credentials.
Разрыв между «найти секрет в коде» и «обнаружить его использование атакующим» - вот где ломается защита. SOC думает, что secret scanning - проблема DevOps. DevOps считает, что мониторинг - задача SOC. Ни один из них не видит полной картины: от коммита до эксплуатации. Пока эти две функции не научатся работать с одним потоком событий, утечки секретов в CI/CD останутся одним из самых дешёвых для атакующего и самых дорогих для защитника векторов. На codeby.net есть тред, где коллеги разбирают интеграцию Jenkins и GitLab audit log в SIEM-корреляцию - конкретные парсеры и правила под эту связку.