Опубликовано 2026-10-27 · Обновлено 2026-10-27
Утёк ключ API на GitHub? Что делать в первый час
Коротко: сначала отзовите или замените утёкший ключ у провайдера, в первые минуты, и только потом занимайтесь Git. Удаление файла или коммита утечку не отменяет: ключ мог быть уже скопирован. Затем проверьте логи использования у провайдера, выкатите новый ключ и лишь после этого чистите историю Git и включайте проверку секретов, чтобы это не повторилось.
Ротация ключа означает выпустить новый и сделать так, чтобы старый перестал работать. Секрет это любое значение, которое подтверждает право приложения что-то делать: ключ API, URL базы данных с паролем, токен. Эта страница устроена как план действий по часам: первые 10 минут, первый час, первые сутки и дни после. Если нужна общая картина, начните с чек-листа безопасности вайб-кодинга.
В этой статье
Первые 10 минут: отзовите или замените ключ Первый час: что мог сделать ключ и пользовался ли им кто-то Поставьте новый ключ в приложение и передеплойте Проверьте сервер и приложение на следы злоупотребления Первые сутки: почистите историю Git после ротации Где ещё прячутся секреты Чего делать не нужно Потом: как не допустить следующей утечки Часто задаваемые вопросыБыстрый результат (10 минут)
- Отзовите или замените утёкший ключ у провайдера прямо сейчас. С Git не начинайте. См. первые 10 минут.
- Откройте логи использования или запросов у провайдера и поищите трафик, расходы или ресурсы, которых вы не создавали.
- Положите новый ключ в переменную окружения или менеджер секретов, передеплойте и убедитесь, что старый ключ отклоняется.
- Выполните
git log --all --oneline -- .env, чтобы узнать, попадал ли файл в коммиты на любой ветке. - Только когда ключ мёртв, планируйте чистку истории и включайте push protection.
Первые 10 минут: отзовите или замените ключ
Сделайте это раньше всего остального, даже раньше, чем откроете репозиторий. В документации GitHub об удалении чувствительных данных из репозитория сказано: если это секрет, первым шагом его нужно отозвать или заменить, а после отзыва по нему уже нельзя получить доступ. Переписывание истории Git не достанет копии, которые уже лежат в чужих клонах, форках и у сканеров, поэтому риск заканчивается только тогда, когда ключ мёртв.
У каждого провайдера свои формулировки. Опирайтесь на документацию вендора и выбирайте самый быстрый способ, после которого старый ключ перестаёт работать:
- OpenAI: создайте замену, обновите приложения, затем отзовите старый ключ. Такой порядок для плановой ротации описан в рекомендациях для продакшена. При живой утечке сначала отзовите ключ и смиритесь с коротким простоем.
- Anthropic: документация по аутентификации советует отключить или удалить любой ключ, который мог утечь. Отключение обратимо, удаление нет.
- Stripe: в рекомендациях по ключам сказано менять раскрытый секретный или ограниченный ключ немедленно, даже если вы не уверены, что его кто-то видел. При ротации можно выбрать, когда старый ключ перестанет работать; при утечке выберите самый ранний вариант. Пройдите чек-лист Stripe для скомпрометированных ключей и замените остальные ключи из того же файла.
- AWS: в блоге AWS Security What to do if you inadvertently expose an AWS access key первым шагом советуют отключить учётные данные, а не удалять их, потому что отключение можно отменить, если приложение сломается.
- Supabase: документация по ключам API говорит удалить утёкший ключ немедленно и предупреждает, что блокировки в браузере мало: атакующий может использовать ключ из других инструментов. Удаление секретного ключа необратимо, поэтому новый должен быть готов заранее. Роль service role обходит row-level security, поэтому этот ключ так важен.
- Ключи сервисных аккаунтов Google Cloud и Firebase: рекомендации Google по управлению ключами требуют, если ключ попал в репозиторий, удалить его в IAM как можно быстрее, потому что системы контроля версий хранят постоянную запись изменений.
- URL или пароль базы данных: смените пароль в базе или у хостинга и обновите все строки подключения. URL содержит пароль, значит это такой же секрет, как любой ключ.
Если вы не уверены, какие ключи были в файле, меняйте все. Лишняя ротация стоит минуты, а пропущенный ключ может стоить намного дороже.
Первый час: что мог сделать ключ и пользовался ли им кто-то
Когда ключ мёртв, оцените ущерб. Важны два вопроса: что ключу было разрешено и что он делал, пока был раскрыт. В AWS описан тот же порядок: определить, к чему дают доступ учётные данные, отключить их, затем проверить аккаунт на остаточный доступ и посмотреть логи.
- Запишите, на что способен ключ. Ограниченный или read-only ключ уменьшает ущерб. Неограниченный секретный ключ или админский облачный ключ нет. Stripe отмечает, что ограниченные ключи существуют именно затем, чтобы ограничить возможности вора.
- Прочитайте записи об использовании у провайдера за окно утечки: от момента коммита до отзыва. Ищите запросы в странное время, из незнакомых мест или в объёмах, которых вы не создавали. Stripe хранит логи запросов, которые можно смотреть даже после удаления ключа, OpenAI показывает использование ключей на странице Usage, у Anthropic есть Usage and Cost API по рабочим пространствам, а AWS указывает на CloudTrail и логи S3-бакетов.
- Ищите то, чего не должно быть: новых облачных пользователей и ключи, новые серверы, новые вебхуки, изменённые реквизиты выплат или подключённые аккаунты (Stripe перечисляет их в своём чек-листе), строки и файлы, которых вы не создавали.
- Если данные можно было изменить или удалить, сравните их с бэкапом до утечки. См. руководство по бэкапам 3-2-1.
- Сохраните доказательства. Сохраните логи и хеши коммитов до того, как что-либо чистить. Они понадобятся, если придётся писать провайдеру или клиентам.
Если провайдер показывает незнакомую активность, напишите в его поддержку и считайте инцидент настоящим. Например, чек-лист Stripe включает отмену неавторизованных выплат и возврат мошеннических платежей и просит начинать быстро.
Поставьте новый ключ в приложение и передеплойте
Теперь верните приложение в строй с ключом, который никогда не был публичным.
- Храните новый ключ вне кода. Используйте переменную окружения или менеджер секретов платформы, но не исходный файл. Подробнее: переменные окружения и управление секретами.
- Обновите все места, где жил старый ключ: прод, стейджинг, настройки CI, serverless-платформы и машины коллег. Документация Supabase советует убедиться, что каждый компонент использует новый ключ, прежде чем убирать старый.
- Передеплойте или перезапустите приложение, чтобы оно прочитало новое значение. Даже когда ключами управляет провайдер, обычно нужен передеплой, как отмечает Stripe для управляемых ключей.
- Убедитесь, что старый ключ отклоняется. Stripe документирует, что запрос с истёкшим ключом возвращает ошибку аутентификации. Сделайте один тестовый вызов со старым ключом и ожидайте отказ.
- Не забудьте сопутствующие секреты. Подписывающий секрет вебхука Stripe отделён от ключей API, так что проверьте, не лежал ли он в том же файле.
Полный порядок первого деплоя: как безопасно развернуть приложение на VPS.
Проверьте сервер и приложение на следы злоупотребления
Если утёкший секрет давал доступ к серверу (закрытый SSH-ключ, токен деплоя, пароль базы, облачный ключ, который управляет машиной), поищите то, что обычно оставляет после себя злоумышленник. Эти команды только читают информацию и ничего не меняют. Выполните их по SSH на сервере:
last -a | head -20
sudo ss -tlnp
ps aux --sort=-%cpu | head -10
sudo cat /root/.ssh/authorized_keys
sudo crontab -l
ls /etc/cron.d
Они показывают последние входы, что слушает сеть, самые загруженные процессы, SSH-ключи, которым разрешён вход под root, и запланированные задания.
- Неожиданные процессы, сильно нагружающие процессор, особенно со случайными именами или запущенные из
/tmp. - Новые SSH-ключи в authorized_keys, которые вы не добавляли, и входы с незнакомых адресов.
- Новые задания cron, пользователи или слушающие порты, которые вы не можете объяснить.
- Незнакомые записи в логах. На Debian и Ubuntu
sudo journalctl -u ssh --since "2 days ago"покажет активность SSH; см. journalctl и мониторинг сервера и анализ логов.
Если что-то выглядит неправильно, перестаньте считать это утёкшим ключом и считайте взломом. Следуйте руководству по реагированию на взлом и сначала прочитайте про реагирование на инциденты и сдерживание. Если всё чисто, вы всё равно сменили ключ, а это было главным.
Не уверены, дошла ли утечка до сервера?
Бесплатная проверка в режиме только чтения покажет, что открыто на сервере и что исправлять в первую очередь.
Получить бесплатную проверку сервера Связаться с инженером по безопасностиПервые сутки: почистите историю Git после ротации
Чистка истории идёт после ротации, а не до. В документации GitHub сказано, что после отзыва или замены секрета переписывание истории «может быть неоправданным», а документация по secret scanning говорит то же самое: удаление секретов из истории требует времени и часто не нужно, если учётные данные отозваны. Чистите историю, если хотите убрать файл ради порядка или если данные нельзя заменить, как персональные данные.
Сначала выясните, попадал ли файл в коммиты на любой ветке:
git log --all --oneline -- .env
Любой вывод означает, что попадал. Чтобы найти само значение ключа, используйте опцию git «pickaxe», которая находит коммиты, добавляющие или удаляющие строку. Берите короткую несекретную часть, например префикс ключа:
git log --all --oneline -S"PREFIX_OF_THE_KEY"
Затем перестаньте отслеживать файл и добавьте его в игнор. Старые коммиты это не затрагивает:
git rm --cached .env
echo ".env" >> .gitignore
Чтобы убрать файл из всех коммитов, GitHub рекомендует git-filter-repo, который проект git тоже рекомендует вместо старого git filter-branch. Нужна версия 2.47 или новее: в ней есть флаг --sensitive-data-removal. Предупреждение: это переписывает историю и после force-push не откатывается. Сначала предупредите коллег. Работайте в свежем клоне:
git clone https://github.com/YOUR-USERNAME/YOUR-REPOSITORY
cd YOUR-REPOSITORY
git-filter-repo --sensitive-data-removal --invert-paths --path .env
Путь нужен полный, от корня репозитория, а если раньше файл лежал по другим путям, добавьте ещё один --path для каждого. Чтобы заменить текст, а не удалять файл, GitHub показывает --replace-text с файлом со списком строк; формат смотрите в руководстве git-filter-repo. BFG Repo-Cleaner более старая альтернатива, но в README git-filter-repo сказано, что BFG умеет лишь несколько видов переписывания, поэтому здесь используется git-filter-repo.
Прежде чем отправлять, убедитесь, что секрета в истории больше нет. GitHub также советует посчитать затронутые pull request командой grep -c '^refs/pull/.*/head$' .git/filter-repo/changed-refs и выбросить клон, если число больше ожидаемого. Затем отправьте:
git push --force --mirror origin
Прочитайте эти оговорки из документации GitHub до того, как отправлять:
- Хеши коммитов меняются у затронутых коммитов и всех последующих, а подписи коммитов и тегов удаляются.
- Push в
refs/pull/завершится ошибкой, потому что GitHub помечает их как доступные только для чтения. Если ошибка у других ссылок, причиной может быть защита веток. - Коллеги должны выбросить старые клоны и клонировать заново либо сделать rebase, а не merge своих веток. Один merge-коммит может вернуть старую историю.
- Если коммит есть в форке, он остаётся там доступным. Нужно попросить владельца форка удалить его.
- Pull request и кэшированные страницы по хешу коммита могут продолжать показывать старые данные. Поддержка GitHub может удалить кэшированные страницы и ссылки в pull request. Сообщите им название репозитория, число затронутых pull request и «First Changed Commit(s)», которые печатает инструмент. GitHub отмечает, что поддержка помогает, только если риск нельзя снять заменой учётных данных.
- Перед переписыванием слейте или закройте открытые pull request, как рекомендует GitHub.
Где ещё прячутся секреты
Исправить репозиторий мало. Секрет, который лежал в одном месте, скорее всего скопировался и в другие. Проверьте каждое из них и считайте любую находку поводом снова менять ключ, если в ней живой ключ:
- Другие ветки, теги и stash. Ключ
--allв командах выше охватывает ветки и теги. Старые файлы.envчасто лежат в давно забытой ветке. - Закоммиченный
.envили файл с примером с настоящими значениями. Как искать и блокировать такие файлы на веб-сервере: открытые .env и .git. - Логи CI и деплоя. Вывод сборки иногда печатает значения окружения. Поищите в свежих логах префикс ключа и по возможности сократите срок хранения логов.
- Слои образов Docker. В документации Docker сказано, что аргументы сборки и переменные окружения не подходят для секретов, потому что остаются в итоговом образе. Используйте секретные монтирования при сборке и пересоберите образы, отправленные в реестр. См. безопасность Docker-хоста и безопасный деплой Docker Compose.
- Фронтенд-бандлы. В документации Vite сказано, что переменные с префиксом
VITE_попадают в клиентский исходный код, и предупреждено, что секретов в них быть не должно. Всё, что скомпилировано в бандл для браузера, публично, поэтому замените ключ и перенесите вызов на сервер. - Скриншоты, записи экрана и общие документы. Ключ, видимый в баг-репорте или обучающем видео, считается опубликованным.
- История чатов, тикеты поддержки и обсуждения задач, включая чаты с ИИ-ассистентом, куда вы вставляли ошибку вместе с ключом.
- Контекст ИИ-ассистента. Ключ в файле проекта, промпте или памяти кодового ассистента это ещё одна копия, которую вы не полностью контролируете. Смените его и следуйте правилам безопасности для ИИ-ассистентов по коду, чтобы ключи не попадали в контекст.
Чего делать не нужно
Это типичные паникующие движения. Каждое кажется прогрессом и ничего не исправляет.
- Не просто удаляйте файл или коммит. Рекомендации Google объясняют почему: большинство систем контроля версий хранят постоянную запись изменений, а копии могут уже существовать. Сделать ключ бесполезным может только провайдер.
- Не ждите, случится ли что-нибудь. В документации Stripe сказано, что злоумышленники постоянно сканируют публичные репозитории в поисках ключей и что ключ нужно менять немедленно, даже если вы не уверены, что его кто-то видел.
- Не переписывайте историю до ротации. Это выглядит как исправление, занимает час и оставляет рабочий ключ в каждой копии.
- Не делайте репозиторий приватным и не считайте дело закрытым. Это не вернёт клоны, форки и скопированные сканерами данные, сделанные, пока репозиторий был публичным.
- Не вставляйте ключ в чат с ИИ или на форум, чтобы попросить о помощи. Так появляется ещё одна копия. Перед вопросом замените значение заглушкой вроде
YOUR_API_KEY.
Потом: как не допустить следующей утечки
Когда пожар потушен, потратьте час на профилактику. Эти изменения дешёвые, а некоторые уже встроены в инструменты, которыми вы пользуетесь.
- Игнорируйте секреты до первого коммита. Добавьте это в
.gitignoreи закоммитьте.env.example, в котором только поддельные значения:.env .env.* !.env.example - Включите защиту GitHub. По документации push protection, push protection для пользователей включена по умолчанию и не даёт отправлять секреты в публичные репозитории. Push protection на уровне репозитория по умолчанию выключена, её должен включить администратор репозитория (или владелец организации, или security manager), и для неё нужен GitHub Secret Protection. По умолчанию любой с правом записи может обойти её, указав причину, поэтому предупреждение считайте знаком «стоп». Secret scanning бесплатен для публичных репозиториев; для приватных нужен Secret Protection на GitHub Team или Enterprise Cloud.
- Добавьте сканер в pre-commit, чтобы секрет ловился на вашей машине. Gitleaks сканирует историю командой
gitleaks git -vи файлы командойgitleaks dir -v ., а в его README описан pre-commit hook; после добавления конфига выполнитеpre-commit install. Второй вариант: TruffleHog; его фильтр--results=verifiedпоказывает только те учётные данные, которые инструмент подтвердил как рабочие, проверив их через API сервиса.
Это фрагмент из README gitleaks; чтобы взять актуальный релиз, выполнитеrepos: - repo: https://github.com/gitleaks/gitleaks rev: v8.24.2 hooks: - id: gitleakspre-commit autoupdate. - Задайте лимиты расходов и оповещения для каждого платного ключа. OpenAI предлагает оповещения о расходах и жёсткие лимиты, которые останавливают API-трафик, а Anthropic позволяет ограничить месячные расходы для каждого рабочего пространства.
- Ограничивайте ключи до необходимого минимума. Stripe рекомендует ограниченные ключи, AWS рекомендует роли или федерацию, а Supabase рекомендует отдельный секретный ключ для каждого компонента бэкенда, чтобы утечка означала замену одного ключа. Это принцип наименьших привилегий.
- Используйте отдельные ключи для разработки и продакшена. У Stripe есть отдельные ключи песочницы и боевого режима, а Anthropic описывает отдельные рабочие пространства для разработки, стейджинга и продакшена. Агентам по коду и коллегам давайте только ключи для разработки.
- Предпочитайте краткоживущие учётные данные. Anthropic поддерживает Workload Identity Federation с краткоживущими токенами и срок действия ключей, AWS рекомендует роли с временными учётными данными, а Google рекомендует вместо ключей прикреплённые сервисные аккаунты.
Как это сочетается с защитой сервера в целом, см. в чек-листе безопасности VPS. Если безопасностью в команде никто не занимается, а такая утечка навредит клиентам, сравните управляемую защиту и самостоятельную.
Ещё для вайб-кодеров: все руководства серии собраны в центре безопасности для вайб-кодеров, а пункты можно отмечать в интерактивном чек-листе.
Часто задаваемые вопросы
Я выложил ключ API на GitHub. Что делать в первую очередь?
Сразу отзовите или замените ключ у провайдера, до того как трогать Git. Затем посмотрите логи использования на незнакомую активность, поставьте новый ключ в приложение и передеплойте. Историю Git чистите только после того, как старый ключ перестал работать.
Безопасен ли ключ API, если репозиторий приватный?
Не обязательно. В документации Stripe отмечено, что даже приватные репозитории могут быть раскрыты через среды разработки, а ключ может утечь ещё и через логи, скриншоты или чаты. Если настоящий ключ был закоммичен, замените его: ротация стоит минуты.
Убирает ли утечку удаление коммита или файла?
Нет. Система контроля версий хранит запись изменений, а ключ мог быть уже склонирован, форкнут или собран сканерами. Рекомендация GitHub для секретов: сначала отозвать или заменить их, а переписывание истории после этого, по его словам, может быть и вовсе неоправданным.
Как удалить секрет из истории Git?
После замены ключа используйте git-filter-repo версии 2.47 или новее с флагом --sensitive-data-removal в свежем клоне, затем сделайте git push --force --mirror origin. Сначала предупредите коллег: переписывание меняет хеши коммитов, и им придётся клонировать заново или делать rebase.
Как понять, пользовался ли кто-то моим утёкшим ключом?
Прочитайте логи использования или запросов у провайдера за время от коммита до отзыва и поищите незнакомый трафик, расходы, новые ресурсы или изменённые данные. Для AWS руководство указывает на CloudTrail и логи S3-бакетов. Также проверьте сервер на новые SSH-ключи, задания cron и процессы.
Отзовёт ли GitHub или провайдер утёкший ключ автоматически?
Иногда, но не рассчитывайте на это. GitHub уведомляет провайдера, когда находит поддерживаемый партнёрский секрет, чтобы тот мог отреагировать, а Stripe пишет, что может деактивировать раскрытые ключи, но обнаружение никто не гарантирует. Меняйте ключ сами.
Нужно ли писать в поддержку GitHub после чистки истории?
Только если нужно удалить кэшированные страницы или ссылки в pull request. Поддержка GitHub может это сделать, если указать название репозитория, число затронутых pull request и First Changed Commit. GitHub отмечает, что помогает она, только когда риск нельзя снять заменой учётных данных.
Не уверены, в каком состоянии ваш сервер?
Получите бесплатную проверку безопасности в режиме только чтения или поговорите с инженером по безопасности об управляемой защите.
Получить бесплатную проверку сервера Связаться с инженером по безопасности