Перейти к содержимому

Сканирование секретов в pre-commit хук: как не пропустить AWS-ключ в коммите

Сканирование секретов в pre-commit хук: как не пропустить AWS-ключ в коммите
Время чтения: 14 мин.

По данным GitGuardian (2024 State of Secrets Sprawl), за один год на публичных репозиториях GitHub обнаружилось 12,8 миллиона новых секретов — API-ключей, токенов, паролей к базам данных. Один утёкший AWS Access Key ID (строка вида AKIAIOSFODNN7EXAMPLE) превращается в тысячи долларов на чужом аккаунте за минуты: боты непрерывно сканируют публичные коммиты и начинают использовать найденные ключи быстрее, чем разработчик наберёт git revert.

Утечка credentials — готовый вектор первоначального доступа (initial access в терминах MITRE ATT&CK). Атакующему не нужно искать уязвимость и не нужен фишинг — он получает валидные ключи прямо из репозитория. По данным Verizon DBIR (2025), украденные учётные данные по-прежнему в числе главных способов проникновения. Ниже — пошаговая настройка gitleaks как pre-commit хука и в GitLab CI pipeline, чтобы секреты не попадали в репозиторий вообще.

Как pre-commit хук ловит секреты до коммита

Pre-commit хук (hook — скрипт, который Git автоматически запускает перед созданием коммита) — первый рубеж защиты от утечки credentials в коде. Механика простая: вы набираете git commit, Git вызывает скрипт из .git/hooks/pre-commit, и если скрипт завершается с ненулевым кодом возврата — коммит отменяется. Официальная документация Git описывает это так: хук pre-commit запускается до того, как вы напечатаете сообщение коммита, и позволяет проверить данные перед его созданием.

Писать shell-скрипты руками для каждой проверки — путь в никуда: папка .git/hooks по умолчанию не попадает в версионный контроль, и синхронизировать скрипты в команде из 30 человек — отдельная боль. Поэтому используют pre-commit framework — утилиту на Python, которая управляет хуками через YAML-конфиг .pre-commit-config.yaml в корне репозитория. Файл хранится в git, и каждый разработчик после pre-commit install получает одинаковый набор проверок.

Для сканирования секретов в pre-commit хуке чаще всего берут gitleaks — инструмент на Go, который сканирует staged-изменения (файлы, добавленные в индекс через git add) по regex-паттернам. Gitleaks из коробки распознаёт более 150 типов секретов: AWS-ключи, GitHub PAT (Personal Access Token — персональный токен доступа к GitHub API), Stripe API keys, JWT-токены (JSON Web Tokens — токены аутентификации), пароли к базам данных и другое. Подход соответствует принципу shift-left security — OWASP DSOMM (DevSecOps Maturity Model — модель зрелости с категориями Build, Patch, Test, Information Gathering, Culture) рекомендует перемещать проверки безопасности как можно раньше в цикл разработки. Секрет перехватывается ещё до того, как попадёт в локальную git-историю.

Место в цепочке защиты: pre-commit хук (локальная машина) → push → GitLab CI pipeline (сервер, второй рубеж) → серверная проверка при приёме коммита (если настроена). Каждый уровень страхует предыдущий: локальный хук можно обойти флагом --no-verify, CI pipeline — нет.

Настройка pre-commit хука для секретов: пошаговая инструкция

Требования к окружению

  • ОС: Linux, macOS или Windows с WSL (Windows Subsystem for Linux)
  • Git: 2.28+ (проверить: git --version)
  • Python: 3.8+ (для pre-commit framework; проверить: python3 --version)
  • RAM: минимальные — gitleaks занимает около 50 МБ при сканировании
  • Сеть: нужна при первой установке хуков (скачивание бинарника gitleaks), далее работает offline
  • Контекст применения: любой git-репозиторий — внутренний, open source, монорепо

Шаг 1. Установка pre-commit framework. Выполните pip install pre-commit (или pip3 install pre-commit, если Python 3 не дефолтный). После установки pre-commit --version должна вернуть номер версии. Вернула — фреймворк на месте.

Шаг 2. Установка gitleaks. На macOS: brew install gitleaks. На Linux: скачайте бинарник со страницы релизов проекта gitleaks на GitHub и поместите в /usr/local/bin/. Gitleaks — единый Go-бинарник без внешних зависимостей, системные библиотеки не нужны. Проверка: gitleaks version должна вернуть номер версии (на момент написания — v8.18+).

Шаг 3. Создание конфига. В корне репозитория создайте файл .pre-commit-config.yaml:

repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.0
    hooks:
      - id: gitleaks

Этот конфиг говорит pre-commit framework: «при каждом коммите запускай gitleaks в режиме protect --staged» — сканируй только те файлы, которые добавлены в индекс через git add, а не весь репозиторий.

Шаг 4. Активация хука. Выполните pre-commit install в корне репозитория. Команда создаст файл .git/hooks/pre-commit, который будет вызывать фреймворк при каждом git commit. Ожидаемый вывод: pre-commit installed at .git/hooks/pre-commit.

Шаг 5. Проверка на всём репозитории. Запустите pre-commit run --all-files. Gitleaks просканирует все файлы и покажет результат. Секретов нет — увидите Passed. Нашлись — вывод покажет файл, номер строки и тип обнаруженного секрета.

Частая ошибка: забыть добавить .pre-commit-config.yaml в git. Файл конфига — часть репозитория, его нужно закоммитить: git add .pre-commit-config.yaml && git commit -m "Add pre-commit config". Без этого коллеги не получат настройку при клонировании.

Ещё одна ловушка: pre-commit install нужно выполнять каждому разработчику после клонирования. Навязать это через сервер нельзя — хук работает только локально. Решение — добавить инструкцию в README или onboarding-скрипт (make setuppip install pre-commit && pre-commit install). Некоторые команды используют общий Docker-образ для разработки, где все инструменты уже настроены — это снимает проблему онбординга новых людей.

Кастомные правила gitleaks для предотвращения утечки AWS-ключей

Gitleaks из коробки ловит AWS Access Key ID по паттерну AKIA[0-9A-Z]{16}. Но AWS Secret Access Key — 40-символьная строка из букв, цифр, / и + — выглядит как обычный случайный пароль, и дефолтные правила могут пропустить его без окружающего контекста. Если в вашей команде используются AWS, стоит добавить кастомное правило, которое ищет Secret Key рядом с характерным именем переменной.

Для кастомных правил используют файл .gitleaks.toml (TOML — текстовый формат конфигов, похожий на INI-файлы). Каждое правило — блок [[rules]] с regex-паттерном, описанием и ключевыми словами для сужения поиска:

[[rules]]
id = "aws-secret-key"
description = "AWS Secret Access Key"
regex = '''(?i)aws_secret_access_key\s*[=:]\s*[A-Za-z0-9/+=]{40}'''
keywords = ["aws_secret"]

[[rules]]
id = "aws-access-key-id"
description = "AWS Access Key ID"
regex = '''AKIA[0-9A-Z]{16}'''
keywords = ["AKIA"]

Разберём компоненты для тех, кто не работал с regex в контексте безопасности. (?i) — регистронезависимый поиск (case-insensitive, найдёт и AWS_SECRET_ACCESS_KEY, и aws_secret_access_key). \s*[=:] — знак = или : с возможными пробелами (в .env файлах используют =, в YAML — :). Секция keywords ускоряет работу: gitleaks сначала ищет подстроку aws_secret в файле и только при совпадении применяет тяжёлый regex. На больших репозиториях разница заметная.

Запуск с кастомным конфигом: gitleaks detect --config .gitleaks.toml --source . --verbose. Флаг --verbose покажет детали каждого обнаружения: файл, номер строки, автора коммита и ID правила, которое сработало.

Обработка ложных срабатываний

В тестовых файлах, документации и fixtures часто встречаются примеры ключей (AKIAIOSFODNN7EXAMPLE). Чтобы gitleaks не блокировал коммиты из-за них, есть два подхода.

Inline-исключение. Добавьте комментарий gitleaks:allow в строке с допустимым значением: TEST_KEY = "AKIAIOSFODNN7EXAMPLE" # gitleaks:allow. Gitleaks увидит метку и пропустит строку.

Allowlist в конфиге. Добавьте раздел [allowlist] в .gitleaks.toml с перечислением путей, которые нужно игнорировать: тестовые fixtures (tests/fixtures/.*), папка с примерами (docs/examples/.*), а также конкретные паттерны-заглушки (EXAMPLE_API_KEY, your-api-key-here).

Для команд с большой legacy-кодовой базой полезен detect-secrets от Yelp — он ведёт файл .secrets.baseline, куда аудированные false positives записываются один раз, и дальше хук алертит только на новые находки. Это снимает проблему «день первый — 500 алертов на старый код, команда отключает хук навсегда».

Интеграция gitleaks в GitLab CI pipeline

Pre-commit хук — первый рубеж, но его можно обойти. Разработчик, который торопится, набирает git commit --no-verify — и хук не запускается. Поэтому нужен второй рубеж: сканирование в CI/CD pipeline (конвейер непрерывной интеграции — система, которая автоматически выполняет заданные шаги при каждом push или merge request в GitLab).

В GitLab CI конвейер настраивается через файл .gitlab-ci.yml в корне репозитория. Добавьте stage security перед build и test (в секции stages:), и создайте job для gitleaks:

secret_detection:
  stage: security
  image: zricethezav/gitleaks:latest
  script:
    - gitleaks detect --source . --log-opts="origin/main..HEAD" --verbose
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == "main"

Что происходит на каждом этапе:

stage: security — job выполняется на этапе security, до build и test. Если gitleaks находит секрет — pipeline падает, код не собирается, merge request блокируется (при включённом флаге «Pipeline must succeed» в настройках проекта GitLab).

image: zricethezav/gitleaks:latest — Docker-образ с gitleaks внутри. Runner (машина, выполняющая CI-задачи) не требует отдельной установки инструмента.

--log-opts="origin/main..HEAD" — ключевой флаг. Gitleaks сканирует не весь репозиторий, а только коммиты между веткой main и текущей HEAD. Это именно то, что появилось в merge request — новые изменения. Полный скан истории при каждом MR был бы слишком медленным и генерировал бы алерты на давно известные (и, возможно, уже отозванные) секреты.

rules — job запускается при merge request events и при push в main.

Ожидаемый результат: при merge request с секретом в коде pipeline помечается как failed, в логе виден файл и строка с обнаружением.

Нюанс для существующих репозиториев. Если вы подключаете gitleaks к проекту с историей, первый запуск полного сканирования (gitleaks detect --source . без --log-opts) может показать десятки секретов в старых коммитах. Не паникуйте. Используйте --log-opts для CI (сканирование только новых изменений), а полный скан истории запускайте отдельно в ручном режиме для аудита. Найденные старые секреты ротируйте по приоритету: сначала те, которые ещё активны.

Зачем оба рубежа? Pre-commit хук даёт мгновенную обратную связь — разработчик видит проблему за секунды, а не после push. CI pipeline даёт гарантию — даже если хук обойдён, секрет не попадёт в основную ветку. Это соответствует рекомендации NIST CSF PR.AA-01 (управление идентификационными данными и учётными записями) и классификации OWASP A02:2021 — Cryptographic Failures, когда чувствительные данные оказываются в открытом виде.

Когда хук обходят: —no-verify и секреты в истории git

Флаг git commit --no-verify пропускает все клиентские хуки, включая gitleaks. Это часть Git, и технически запретить его на локальной машине нельзя. Три варианта решения:

CI как страховка. Если gitleaks стоит в pipeline (как описано выше), обход локального хука не поможет. При push секрет будет обнаружен на сервере, pipeline упадёт, merge request не пройдёт.

Server-side pre-receive hook. GitLab активно развивает встроенную серверную функцию secret detection — сканирование новых blob-объектов через quarantine directory при git push, ещё до принятия коммитов. Согласно issue #422574 в репозитории GitLab, подход масштабируется с размером изменения, а не с размером репозитория, и не требует конфигурации на стороне клиента. На момент написания функция в разработке для GitLab.com и Dedicated, но на self-managed GitLab Enterprise кастомные серверные хуки можно настроить уже сейчас.

Культура команды. Объясните разработчикам, зачем нужен хук. Тот, кто понимает, что утечка AWS-ключа = инцидент + экстренная ротация всех credentials + постмортем + потенциальные финансовые потери, реже тянется к --no-verify. Добавьте в README: «Хук проверяет коммит за 2-3 секунды. Если горит — используйте --no-verify, но CI всё равно поймает проблему при push».

Когда AWS-ключ уже попал в историю. Простой git revert не поможет — после удаления файла секрет остаётся в истории коммитов. Порядок действий:

  1. Немедленно отзовите ключ. AWS IAM Console → Access Key → Deactivate/Delete. Ротация ключа — приоритет номер один, до любых действий с git-историей. Пока вы чистите историю, ключ может использоваться.
  2. Очистите историю. BFG Repo-Cleaner (утилита для массового удаления данных из git-истории) справится быстрее и безопаснее, чем git filter-branch. Команда bfg --replace-text passwords.txt заменит указанные строки на ***REMOVED*** во всех коммитах.
  3. Force-push. После BFG выполните git reflog expire --expire=now --all && git gc --prune=now --aggressive, затем git push --force. Предупредите команду: force-push переписывает историю, всем придётся переклонировать репозиторий или сделать hard reset.
  4. Проверьте CloudTrail. Если ключ был в публичном репозитории хотя бы несколько минут — ищите в AWS CloudTrail API-вызовы от скомпрометированного Access Key ID. Боты сканируют GitHub в реальном времени.

Gitleaks vs TruffleHog: что выбрать для сканирования коммитов

Gitleaks и TruffleHog — два самых популярных open-source инструмента для secrets detection (обнаружения секретов в коде). Оба написаны на Go, оба работают как pre-commit хуки и в CI. Но архитектурно они решают задачу по-разному.

Критерий Gitleaks TruffleHog
Принцип детекции Regex-паттерны + entropy Regex + entropy + верификация API-вызовом
Правила из коробки 150+ типов секретов 800+ детекторов
Скорость в pre-commit Быстро (секунды) Медленнее из-за верификации
Ложные срабатывания Средний уровень Низкий (верификация подтверждает: ключ живой или нет)
Сканирование за пределами git Нет — git, файлы, stdin Да — S3, Docker-образы, Slack, CircleCI
Сетевые вызовы Нет — полностью offline Да — при верификации обращается к API сервисов
Когда использовать Pre-commit хук, CI pipeline для git Полный аудит, пост-инцидентное сканирование
Когда НЕ использовать Нужна проверка live-ключей Air-gapped среда; нужна минимальная задержка в хуке

Ключевое отличие TruffleHog — верификация. Когда TruffleHog находит строку, похожую на Stripe API key, он делает тестовый запрос к API Stripe. Ответ 200 — ключ живой, подтверждённая утечка. Ответ 401 — ключ уже отозван. Это радикально снижает false positive rate, но добавляет задержку и сетевые вызовы — не всегда приемлемо в pre-commit хуке.

Мой выбор для pre-commit — gitleaks: быстрый, без сетевых вызовов, предсказуемое время работы. TruffleHog хорош для периодических аудитов всего репозитория или сканирования нестандартных источников. Многие команды используют оба: gitleaks в pre-commit и CI для ежедневной работы, TruffleHog — для еженедельных или ежемесячных полных проверок.

Отдельно стоит упомянуть detect-secrets (Yelp) и git-secrets (AWS Labs). detect-secrets полезен для больших legacy-кодовых баз благодаря механизму baseline (описан выше). git-secrets — минимальный shell-скрипт без зависимостей, но поддерживает только AWS-паттерны и фактически не обновляется. Для нового проекта в 2025 году git-secrets слишком узкий.

Ограничения: когда сканирование секретов не спасает

Ни gitleaks, ни TruffleHog не решают проблему полностью. Понимание границ инструмента — часть экспертизы, и если вы только входите в ИБ, привыкайте к этой мысли сразу.

Секрет в нестандартном формате. Если ваш внутренний API использует токены без характерного префикса (нет AKIA, sk_live_, ghp_), regex не сработает. Нужны кастомные правила в .gitleaks.toml для каждого формата — и дисциплина их обновления при появлении новых сервисов.

Секрет в бинарном файле. Gitleaks по умолчанию не сканирует бинарники и архивы. В последних версиях добавлена поддержка --max-archive-depth для zip/tar, но .jar с захардкоженным паролем всё равно может пройти незамеченным.

Утечка за пределами git. Gitleaks проверяет код, а не Slack, Confluence или email. Разработчик может скопировать ключ в мессенджер — и сканирование репозитория тут бессильно.

Переменные окружения в логах CI. Если pipeline печатает значение $AWS_SECRET_ACCESS_KEY в лог — это отдельная категория проблемы. Используйте маскинг переменных в GitLab CI Settings → CI/CD → Variables (флаг «Masked»).

Кодирование и обфускация. Base64-кодирование секрета или разбиение строки на части ("AK" + "IA" + "IOSF...") обманет regex-based инструменты. Для случайной утечки нерелевантно, но при целенаправленном обходе — вполне реально.

Сканирование секретов в pre-commit хуке — один уровень в модели OWASP DSOMM, не единственная мера. Полноценная защита от утечки credentials в коде включает: vault-решения для управления секретами (HashiCorp Vault, AWS Secrets Manager), ротацию credentials по расписанию, минимальные привилегии для каждого ключа (least privilege — принцип, при котором ключ получает только те разрешения, которые нужны для конкретной задачи, по NIST CSF PR.AA-01) и мониторинг использования ключей через CloudTrail.

Два года назад я настраивал gitleaks для команды из 35 разработчиков и видел повторяющийся сценарий: хук работает, первые две недели все довольны, потом кто-то находит --no-verify и начинает «экономить три секунды». Через месяц треть команды коммитит без проверки. Формальное наличие хука создаёт ложное чувство безопасности — и это опаснее, чем отсутствие хука вообще, потому что никто не ждёт утечки, «ведь у нас же pre-commit стоит».

Моя позиция жёсткая: pre-commit хук — ремень безопасности, GitLab CI stage — подушка безопасности. Ремень можно не пристегнуть, подушку — нет. Если в вашем пайплайне нет stage security с gitleaks, считайте, что сканирования секретов у вас нет — локальный хук это конвенция, а не гарантия. Начните с CI, потом добавьте pre-commit для быстрого фидбека разработчикам — не наоборот.

Второе наблюдение: подавляющее большинство утечек AWS-ключей, которые я разбирал, происходили не из-за отсутствия инструментов, а потому что ключи создавались с Admin-привилегиями «для быстрого теста» и никогда не ротировались. Gitleaks ловит симптом; правильное управление секретами — лечит причину. Если безопасность CI/CD стала вашей точкой входа в ИБ и хочется выстроить фундамент системно, а не собирать из разрозненных гайдов — IB Basics на Codeby закрывает базу за пару месяцев, от модели угроз до первых практических задач.

Эту тему и смежные навыки разбирают на практике в курсе «Основы практик DevOps» Codeby Academy.