Перед публикацией части внутреннего проекта разработчики наконец запускают сканер секретов по всему репозиторию. Отчёт получается неудобным: среди тестовых строк и хешей находится действующий ключ к облачному хранилищу. В текущей ветке его нет уже два года, но старый коммит никуда не делся. Рядом обнаруживается удалённый CSV с именами, телефонами и адресами клиентов, который когда-то принесли «на час, чтобы воспроизвести баг».
Это две разные проблемы. Ключ можно отозвать и перевыпустить. С выгрузкой клиентов так не получится: нужно понять, кто видел репозиторий, успели ли данные разойтись по клонам и форкам и какие внутренние или регуляторные процедуры теперь запускать. Но начинаются обе проблемы одинаково — с файла, который казался временным, и с ошибочного ощущения, что удаление в следующем коммите всё исправляет.
Git помнит больше, чем показывает рабочая директория. Поэтому проверка репозитория на утечки — не команда, которую однажды запускают перед аудитом. Это цепочка разных проверок: по истории, в локальном Git, в CI и глазами ревьюера. У каждой своё окно видимости, и ни одна не заменяет остальные.
Что именно попадает в репозиторий
Самый узнаваемый случай — закоммиченный .env. Но на практике чувствительные данные редко лежат в файле с настолько говорящим именем. Токен CI прячется в YAML, ключ внешнего API — в интеграционном тесте, строка подключения к базе — в старом конфиге мигратора. Приватный ключ могут вставить в документацию как «рабочий пример», а затем забыть заменить.
Персональные данные приходят другим путём. Разработчик копирует несколько строк из продакшена в фикстуру. Инженер прикладывает к задаче лог с email и IP-адресом, а потом коммитит его вместе с исправлением. Для воспроизведения ошибки в репозиторий кладут дамп базы, CSV-выгрузку или скриншот из админки. Такие файлы часто называют временными, но Git не различает временное и постоянное.
Риск давно не сводится к сценарию «кто-то нашёл пароль». Облачный ключ открывает хранилища и может создать прямые расходы, токен CI даёт доступ к сборкам и артефактам, а утечка персональных данных добавляет требования из области privacy и compliance. Конкретные обязанности зависят от юрисдикции и обстоятельств; здесь нет универсального юридического рецепта. Инженерный вывод проще: в плане реагирования должны быть не только разработчики и DevOps, но и человек, отвечающий за безопасность и работу с персональными данными.
По данным GitHub, за 2024 год его системы обнаружили более 39 миллионов секретов, попавших в публичные репозитории. Это число стоит воспринимать не как рейтинг платформы, а как напоминание о масштабе автоматического поиска публичного кода. Утечку ищете не только вы.
Секрет и ПДн ищутся по-разному
Секрет даёт доступ к системе: это пароль, API-ключ, токен или приватный ключ. У многих провайдеров свои узнаваемые форматы. Такие значения часто похожи на случайные строки, а найденный ключ иногда можно проверить запросом к выпустившему его сервису. Контекст полезен, но обычно не меняет сути: рабочий токен остаётся секретом и в конфиге, и в README.
С персональными данными всё наоборот. Email, телефон, имя, адрес, номер документа или связка «IP + имя» становятся чувствительными не из-за внешнего вида строки, а потому, что помогают прямо или косвенно определить человека. Тот же телефон может принадлежать реальному клиенту, быть номером офиса или вымышленным примером. Набор цифр может оказаться номером паспорта, датой или частью тестового идентификатора.
| Вопрос | Секреты | Персональные данные |
|---|---|---|
| Что является находкой | Значение, которое предоставляет доступ | Данные, позволяющие определить человека в конкретном контексте |
| Хороший первый фильтр | Формат провайдера, ключевое слово, высокая энтропия | Формат, имя поля, соседние данные, словарь домена |
| Можно ли подтвердить автоматически | Иногда — проверкой у провайдера | Обычно нет: нужен контекст и решение человека |
| Основной источник шума | Хеши, UUID, тестовые токены, случайные строки | Примеры, синтетические фикстуры, служебные контакты, похожие номера |
| Первая реакция | Отзыв или ротация | Оценка состава данных, затронутых людей и круга доступа |
Поэтому универсальный «regex для всех ПДн» не работает. Регулярное выражение выдаст кандидатов, но не определит их смысл. Для секретов это тоже не абсолютный ответ, однако форматы провайдеров делают задачу заметно более определённой.
Первый аудит: смотрим не только текущую ветку
Если репозиторий раньше не проверяли, начинать с pre-commit-хука поздно. Хук остановит следующий неудачный коммит, но ничего не скажет о том, что лежит в истории пять лет.
Для первичного аудита нужен скан всех веток и коммитов. Gitleaks умеет проверять рабочее дерево и историю Git, используя правила на регулярных выражениях и, где настроено, пороги энтропии. При выборе стоит учесть текущий статус проекта: автор объявил Gitleaks функционально завершённым и выпускает для него только исправления безопасности. TruffleHog тоже проходит историю, а для поддерживаемых типов умеет проверять найденные учётные данные у провайдера. Его режим --only-verified удобен на первом проходе: из большого отчёта можно сразу вынести наверх активные ключи. Это не значит, что остальные находки безопасны; они просто ждут второй очереди разбора.
У такого аудита две цели. Первая — быстро закрыть действующий доступ. Вторая — понять профиль репозитория: какие файлы дают основной шум, какие сервисы встречаются чаще, сколько старых находок придётся признать известным долгом. Без этого команда обычно включает блокирующий CI, получает длинный красный отчёт и через несколько дней выключает проверку целиком.
Git хранит коммит как снимок дерева и связь с родителями. Удалённый файл продолжает быть доступен из старого коммита, пока тот достижим через ветку, тег, удалённую ссылку или reflog. Сборщик мусора удаляет только недостижимые объекты; локальная очистка не затронет копии коллег и удалённый сервер. Отсюда рабочее правило: всё, что однажды попало в публичный репозиторий, следует считать скопированным.
История Toyota T-Connect хорошо показывает цену задержки. В 2022 году компания сообщила о ключе доступа к серверу с данными клиентов, оставленном в публичном GitHub-репозитории. Он был открыт с 2017 года; потенциально ситуация затронула около 300 тысяч клиентов. Удалить строку спустя годы было недостаточно — ключ пришлось менять, а инцидент разбирать как утечку доступа к данным.
Где поставить проверки
После первичного аудита нужен контроль новых изменений. Здесь полезно думать не названиями продуктов, а моментами, когда утечку ещё можно остановить.
| Точка контроля | Что проверять | Когда запускать | Что останется за бортом |
|---|---|---|---|
| Pre-commit | Добавляемые строки и staged-файлы | Перед каждым коммитом | Старая история; проверку на рабочей машине могут отключить |
| Pre-push | Диапазон локальных коммитов | Перед отправкой на сервер | Уже опубликованные ветки и другие способы загрузки файлов |
| CI для PR/push | Новые коммиты и diff | На каждое изменение | Секрет уже успел попасть на сервер Git |
| Ночной скан | Репозиторий и при необходимости всю историю | По расписанию | Реакция не мгновенная |
| Сканер хостинга | Код и дополнительные сущности платформы | Постоянно | Возможности и тарифы конкретной платформы |
| Код-ревью | Смысл файла и данных | Перед слиянием | Человек может не заметить длинную случайную строку |
Локальная проверка должна быть быстрой. Если она добавляет к каждому коммиту минуту, её начнут воспринимать как помеху. Здесь уместны git-secrets, Gitleaks или detect-secrets через фреймворк pre-commit. git-secrets особенно прост, когда команда хочет закрыть известные шаблоны, например ключи AWS, и готова поддерживать собственные правила. detect-secrets удобен в старом проекте благодаря baseline: текущие находки фиксируются как известные, а новые уже ломают проверку.
Baseline не равен разрешению хранить секреты. Это способ внедрить контроль, не останавливая разработку на неделю. У каждой записи должен быть владелец, причина и дата пересмотра. То же относится к allowlist: бессрочное исключение папки fixtures быстро превращает её в удобное место для реальных данных.
CI нужен независимо от локальных хуков. Он проверяет правила в одном и том же окружении и не зависит от настройки ноутбука. Для публичных репозиториев GitHub автоматически выполняет secret scanning; push protection может остановить известный секрет до записи в репозиторий. При совпадении с форматом партнёра платформа отправляет сигнал провайдеру, и тот может проверить или отозвать ключ. В GitLab Secret Detection запускается как CI-задача. В обычном режиме GitLab смотрит текущее состояние и будущие коммиты, а старую историю нужно просканировать отдельно.
Ночной прогон полезен не потому, что дневной CI ненадёжен. Правила обновляются, появляются новые форматы токенов, и повторный проход по истории может найти то, чего вчерашний набор правил ещё не знал. Issues, комментарии и wiki находятся вне Git-репозитория; их должен проверять нативный сканер платформы, а не локальный Gitleaks или TruffleHog.
Почему сканеры шумят
Большая часть сканеров комбинирует три техники.
Сигнатуры и регулярные выражения ловят известные форматы. Для токена GitHub или ключа AWS это хороший сигнал: префикс и длина заметно сужают поиск. Но неизвестный или внутренний формат пройдёт мимо, пока для него не появится правило.
Энтропия оценивает, насколько строка похожа на случайную. Так находятся ключи без узнаваемого префикса. Вместе с ними в отчёт попадают хеши, UUID, base64, контрольные суммы и сжатые данные. Поэтому порог энтропии приходится настраивать на собственном коде, а lock-файлы и сгенерированные артефакты — разбирать отдельно, а не бездумно исключать всё дерево.
Проверка валидности обращается к сервису, который выпустил ключ. TruffleHog, например, разделяет подтверждённые, неподтверждённые и не проверенные из-за ошибки значения. Для приоритизации это сильный инструмент, но не универсальный: не у каждого секрета есть безопасный проверочный API, а сам скан требует сетевого доступа.
Есть и более тяжёлые варианты. GitGuardian ggshield выполняет проверку через облачный API, поэтому требует аккаунт и отправляет данные на внешний сервис для анализа. Semgrep Secrets добавляет анализ потоков данных и валидаторы, но это коммерческий модуль платформы. Выбор здесь зависит не от количества детекторов на странице продукта, а от правил вашей компании: можно ли отправлять фрагменты кода наружу, нужен ли полностью локальный запуск, кто будет разбирать алерты и сколько репозиториев предстоит покрыть.
Ручной аудит остаётся частью системы. Ревьюер быстрее сканера поймёт, почему в каталоге тестов лежит выгрузка клиентов, но хуже заметит один токен среди тысячи строк конфигурации. Автоматика и ревью закрывают разные типы ошибок.
С ПДн придётся строить свой контекст
У зрелых сканеров секретов есть каталоги форматов и интеграции с провайдерами. У поиска персональных данных в исходном коде такого общего стандарта нет. Платформенный secret scanning документирует работу с учётными данными, а не с телефонами, именами и адресами.
Начать всё равно можно с форматов: email, российские телефоны, ИНН, СНИЛС, серии и номера паспортов. Но результат такого скана нельзя сразу превращать в блокировку слияния. Сначала полезно собрать статистику на своём репозитории и посмотреть, откуда приходят совпадения. Обычно точность повышают не усложнением одного выражения, а контекстом: именем поля (customer_email, passport_number), соседними колонками CSV, расположением файла, словарём внутренних доменов и сочетанием нескольких идентификаторов.
Microsoft Presidio показывает типичный набор техник для анализа текста: регулярные выражения, словари, распознавание сущностей и слова-маркеры рядом с совпадением. Но Presidio не является сканером Git. Чтобы использовать подобный движок, придётся отдельно обходить файлы и историю, учитывать языки программирования, собирать отчёт и подключать CI.
Для ПДн разумно начинать с предупреждений, а не с жёсткой блокировки. Подтверждённая выгрузка клиентов должна остановить публикацию немедленно; одиночный email в примере документации требует проверки, а не аварии. Решение всё равно принимает человек, который знает источник данных и правила конкретной юрисдикции.
Нашли секрет: сначала отзовите, потом чистите
Типичная ошибка после алерта — немедленно удалить строку, переписать последний коммит и решить, что проблема закрыта. Если ключ уже был отправлен на сервер, порядок другой.
- Отозвать или ротировать доступ. Не ждать окончания расследования. Для публичного репозитория ключ считается скомпрометированным, даже если в журнале использования пока пусто.
- Проверить журналы провайдера. Важно понять, использовался ли ключ, к каким ресурсам обращались и не созданы ли новые учётные данные.
- Определить охват. Какие ветки, теги, артефакты CI, форки и клоны содержат значение? Если рядом были ПДн, кто мог их получить?
- Убрать данные из текущей версии. Это остановит новое распространение, но не очистит историю.
- Решить, нужно ли переписывание истории. Для уже отозванного секрета оно не возвращает безопасность и может оказаться дороже пользы. Для персональных или других данных, чья чувствительность не исчезает после отзыва доступа, очистка может быть необходима.
- Зафиксировать инцидент и исправить процесс. Новое правило, обязательный CI, изменение работы с дампами или секрет-хранилище важнее красивого постмортема без действий.
Для переписывания истории сейчас обычно используют git filter-repo; документация Git прямо не рекомендует старый git filter-branch. BFG Repo-Cleaner удобен для массового удаления больших или проблемных blob-объектов. Оба инструмента меняют идентификаторы коммитов и требуют координации: старый клон способен вернуть удалённые данные обратно. Форки, чужие клоны и кэшированные страницы хостинга отдельно не исчезнут; для серверной очистки может понадобиться поддержка платформы.
Если подтверждена утечка ПДн, технической чисткой работа не заканчивается. Например, GDPR предусматривает уведомление надзорного органа в течение 72 часов после обнаружения нарушения, когда оно создаёт риск для прав и свобод людей. В других юрисдикциях сроки и критерии отличаются. Команде не нужно импровизировать юридическую процедуру во время инцидента — маршрут эскалации должен быть известен заранее.
Рабочий минимум для небольшой команды
Не начинайте с покупки большой платформы. Сначала закройте разрывы в процессе.
- Один раз просканируйте всю историю Gitleaks или TruffleHog и разберите активные ключи.
- Поставьте быстрый pre-commit-хук на новые изменения.
- Продублируйте ту же политику в CI, где её нельзя потерять вместе с настройкой ноутбука.
- Включите штатный secret scanning у хостинга и проверьте, требуется ли отдельный исторический режим.
- Запускайте полный скан по расписанию после обновления правил.
- Для ПДн соберите собственные контекстные правила и сначала включите предупреждения.
Дальше нужен не ещё один сканер, а владелец процесса. Кто получает алерт? За сколько времени подтверждает находку? Кто умеет отозвать облачный ключ? Кому уходит сообщение о возможной утечке ПДн? Если ответы существуют только в голове одного DevOps-инженера, автоматизация ещё не закончена.
Чек-лист перед включением блокировки
- [ ] Полная история уже просканирована, старые активные секреты отозваны.
- [ ] Новые секреты проверяются локально и в CI.
- [ ] Известно, какие ветки и типы событий охватывает каждый скан.
- [ ] Allowlist хранит причину, владельца и срок пересмотра исключения.
- [ ] Lock-файлы, сгенерированный код и тестовые фикстуры не исключены целиком без анализа.
- [ ] Алерты разделены хотя бы на «действующий секрет», «кандидат» и «ПДн на проверку».
- [ ] Полные найденные значения не печатаются в логах CI и уведомлениях.
- [ ] Есть человек, который может немедленно отозвать ключ и проверить журналы доступа.
- [ ] Для возможной утечки ПДн известен маршрут эскалации.
- [ ] После инцидента меняется правило или процесс, а не только строка в Git.
Не ищите идеальный сканер
Надёжная схема выглядит буднично: один инструмент проходит старую историю, другой быстро проверяет diff, CI повторяет контроль на сервере, хостинг следит за известными форматами, а человек разбирает контекст. Она не обещает нулевой риск. Зато у неё нет единственной точки отказа.
Секреты хорошо поддаются автоматизации, когда известен формат или доступ можно проверить у провайдера. ПДн требуют знания данных и юридического контекста, поэтому полностью автоматическое решение здесь скорее создаст поток ложной уверенности или ложных тревог. Полезная цель сканирования скромнее: рано показать подозрительное изменение нужному человеку и не дать подтверждённой утечке пройти дальше.
А если секрет уже оказался в публичном Git, спор о качестве сканера можно отложить. Сначала отзовите ключ.