Секрет попал в Git: что делать прямо сейчас
Практика3 минРедакция Innovialabs
Коротко
Сначала отзовите и замените утёкший ключ, затем проверьте логи на подозрительное использование, после этого вычистите историю через git-filter-repo и поставьте gitleaks в pre-commit и CI, чтобы это не повторилось.
Порядок действий
Если ключ, пароль или токен оказался в коммите, действуйте именно в таком порядке:
- Отзовите и замените секрет. Это первый и самый важный шаг.
- Проверьте логи доступа за период с момента коммита.
- Вычистите историю репозитория.
- Настройте защиту, чтобы это не повторилось.
Чистка истории без замены ключа почти ничего не даёт: секрет уже могли скопировать.
Шаг 1. Отзыв и ротация
Считайте секрет скомпрометированным, даже если репозиторий приватный и коммит удалили через минуту. Копии остаются у всех, кто успел сделать clone или pull, в форках, кешах CI и у ботов, которые сканируют публичные репозитории.
- Создайте новый ключ в панели сервиса.
- Обновите его в секрет-хранилище и задеплойте.
- Убедитесь, что всё работает, и отзовите старый ключ.
- Если быстро сменить нельзя (например, пароль от базы с множеством клиентов), временно ограничьте доступ по сети и сменяйте как можно скорее.
Удалить строку новым коммитом — не решение: секрет остаётся в истории.
Шаг 2. Проверка логов
Посмотрите, использовали ли ключ посторонние, начиная с момента коммита:
- журналы доступа и аудита сервиса (у облаков — аудит-логи вроде AWS CloudTrail);
- запросы с незнакомых IP-адресов и в нетипичное время;
- новые ресурсы, пользователи, вебхуки, которые вы не создавали;
- неожиданный рост счетов и расхода лимитов.
Если следы есть — это уже инцидент: выясните, к каким данным был доступ. Если затронуты персональные данные, проверьте, нужно ли уведомить пользователей или регулятора по требованиям вашей юрисдикции.
Шаг 3. Чистка истории
Коммит ещё не отправлен в удалённый репозиторий — просто перепишите его локально:
git rm --cached .env
echo ".env" >> .gitignore
git add .gitignore
git commit --amend
Если секрет был в более раннем коммите, понадобится rebase или инструмент ниже.
Коммит уже в удалённом репозитории — используйте git-filter-repo. Работайте в свежем клоне:
git clone git@github.com:org/repo.git repo-clean
cd repo-clean
# удалить файл из всей истории
git filter-repo --path .env --invert-paths
# или заменить саму строку секрета во всех файлах
git filter-repo --replace-text ../replacements.txt
Файл replacements.txt содержит строки вида старое_значение==>REMOVED. Держите его вне репозитория и удалите после работы.
git-filter-repo в целях безопасности удаляет remote origin, поэтому добавьте его заново и отправьте переписанную историю:
git remote add origin git@github.com:org/repo.git
git push origin --force --all
git push origin --force --tags
После этого:
- Предупредите команду: всем нужно заново клонировать репозиторий, а не делать pull. Иначе старая история вернётся при следующем merge.
- Проверьте, что переписаны все ветки и теги.
- На GitHub и подобных платформах старые коммиты могут оставаться доступны по прямой ссылке, в форках и pull request’ах. Для их удаления обращайтесь в поддержку платформы.
Шаг 4. Защита от повторения
gitleaks в pre-commit проверяет изменения до коммита. Удобно подключить через фреймворк pre-commit:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0 # укажите актуальный релиз
hooks:
- id: gitleaks
Затем выполните pre-commit install — хук будет запускаться при каждом коммите.
Локальный хук можно обойти флагом --no-verify, поэтому добавьте проверку в CI: тот же gitleaks по всем изменениям в pull request. Для разового сканирования всей истории подойдёт gitleaks git -v (в старых версиях — gitleaks detect).
Дополнительно:
.envи похожие файлы — в.gitignoreс первого дня, в репозитории только.env.example.- Включите сканирование секретов и защиту при push, если ваша платформа это поддерживает.
- Храните секреты в менеджере секретов или хранилище CI, а не в файлах проекта.
Частые ошибки
- Начать с чистки истории и забыть сменить ключ.
- Сделать
git rmновым коммитом и считать проблему решённой. - Не предупредить команду, и кто-то снова отправляет старую историю.
- Положиться только на локальный хук без проверки в CI.