14 сентября 2025 года я обнаружил действующий секрет в публичном репозитории на GitHub — личный токен доступа был залит в конфигурационный файл и оставался в истории коммитов. Описываю, как именно я нашёл утечку, как эскалировал инцидент, что ответил автор и какие шаги были предприняты для исправления.
Как нашёл проблему
Работая над инструментом анализа зависимости для внутреннего клиента, я периодически запускаю массовые проверки открытых репозиториев на признаки утечки ключей и конфигураций. 2025-09-14 я прогнал выборку из 12 тысяч репозиториев по шаблонам, связанным с переменными окружения и форм-факторами GitHub-токенов. В одном репозитории (публичный, около 1.2K коммитов, 54 ветки) мой поиск вернул совпадение в файле config/.env, где содержался токен, совпадавший по формату с GitHub Personal Access Token.
Шаблоны и инструменты
В проверке использовались сочетания готовых инструментов и собственных регулярных выражений. Основной набор:
- rg (ripgrep) 14.0 с флагами для рекурсивного поиска по скрытым файлам;
- git log --all для поиска по истории коммитов;
- gitleaks v8 для валидации найденного совпадения;
- GitHub API для верификации действительности токена.
Пример команды, которая вернула первичный матч (в моём наборе работал с ru/utf8):
rg -n --hidden --glob "!node_modules" "(github_pat_|ghp_)[A-Za-z0-9_\-]{20,100}" ./repos-list/2025-09-14/
Первичный регулярный шаблон искал распространённые префиксы токенов: github_pat_ и ghp_. После совпадения я запустил gitleaks для проверки контекста и истории:
gitleaks detect --source ./repos/repo-x --report-path ./reports/repo-x-gitleaks-2025-09-14.json
gitleaks указал, что совпадение появляется в файле config/.env и присутствовало в трёх старых коммитах. Дальнейшая проверка с помощью git log -S показала, что секрет был добавлен 2024-11-03, повторно появлялся при мердже веток в 2025-03 и 2025-07.
Проверка активности токена
После подтверждения формата я проверил действительность токена аккуратным образом, используя GitHub API с минимально возможными запросами, чтобы не злоупотреблять учётными данными. Метод: отправить unauthenticated запрос на эндпоинт, который покажет, действует ли токен (HEAD-запрос с токеном в заголовке).
# Пример проверки (не делайте это с токеном в логах)
curl -I -H "Authorization: token ghp_xxx..." https://api.github.com/user
Ответ 200 подтверждал, что токен активен; ответ 401/403 означал бы, что токен недействителен или отозван. В моём случае токен был активен и имел права на чтение приватных репозиториев организации, что увеличивало риск компрометации.
Как эскалировал
Действовал по принципам ответственного раскрытия. Моя цель — дать владельцу проекта возможность быстро устранить утечку и минимизировать ущерб, не привлекая лишнего внимания.
Шаги эскалации
- Собрал доказательства: снимки экрана, результаты gitleaks-отчёта, пруфы работы токена (HTTP-коды), список файлов и хеши коммитов. В отчёте я избегал публикации самого токена — вместо этого использовал маскировку (ghp_***).
- Наметил контакты: email из профиля GitHub, ссылка на issue tracker, контакт в README. Если контактной информации не было — использовал форму "Report a vulnerability" у GitHub и e-mail security@domain, если он указан.
- Отправил приватное сообщение с пометкой "Security" и ссылкой на GitHub Security Advisories (если владелец его поддерживает). Первичное письмо отправлено 2025-09-14 в 21:12 UTC, с просьбой ответить в течение 48 часов и указанием минимальных шагов для быстрого нейтрализации.
Шаблон сообщения, который я отправил (сокращённый, без раскрытия токена):
Subject: [SECURITY] Potential GitHub token exposed in public repo
Hello @owner,
I found what appears to be a live GitHub Personal Access Token inside the file "config/.env" in commit 9f3a4b... (masked). The token seems active and grants read access to private repos.
Recommended immediate actions:
1) Revoke the token in https://github.com/settings/tokens
2) Rotate credentials used by CI
3) Remove the token from the repo history (see suggested commands below)
Please respond with ETA for remediation. If I do not hear back within 48 hours, I will report the token to GitHub through their disclosure channels.
Regards,
Security researcher
Параллельно с письмом я создал приватный тикет в GitHub (если репозиторий его поддерживал) и, при отсутствии реакции, подал репорт через логичный внутренний процесс и инструмент GitHub: https://github.com/contact/report-program-issue (через веб-интерфейс безопасности).
Сроки и коммуникация
Я дал владельцу 48 часов для первичного ответа. Если за это время не было реакции, следующий шаг — уведомление GitHub Security. В данном случае автор ответил в течение 18 часов (2025-09-15 15:44 UTC), что позволило действовать напрямую.
Что ответил автор
Автор репозитория подтвердил получение сообщения и признал, что секрет попал в репозиторий по ошибке при переносе локальной конфигурации. Его ответ был отправлен 2025-09-15 и содержал следующие ключевые тезисы.
"Спасибо за уведомление — токен оказался в ветке legacy после слияния. Я уже начал процедуру отзыва и очистки истории."
Дальнейшее содержание ответа включало:
- Подтверждение, что токен принадлежал одному из разработчиков и использовался для локальной работы с GitHub API;
- Обещание отозвать токен в ближайшие 30 минут и сообщить о завершении;
- Планы по очистке истории с использованием git-filter-repo и по добавлению правил .gitignore и pre-commit хука для предотвращения повторений.
Автор прислал ссылки на приватный тикет и обновлённый план действий. Полный текст ответа выглядел примерно так (редакция):
Hi,
Thanks for reporting. The token was added by mistake during a merge with a legacy branch. I will revoke it now and push remediation steps. ETA ~1 hour.
Steps we will take:
- Revoke token via GitHub settings
- Rotate any dependent secrets
- Use git-filter-repo to remove token from history
- Add .env to .gitignore and add pre-commit githook
I will update you when done.
Как исправили
Исправление прошло в несколько шагов: немедленная нейтрализация, очистка истории, изменения в инфраструктуре и пересмотр практик работы с секретами.
1) Немедленная нейтрализация
Автор отозвал персональный токен в настройках GitHub в 2025-09-15 16:02 UTC. Это снизило риск незамедлительного использования токена. Одновременно он проверил список приложений и не нашёл признаков злоупотребления.
2) Очистка истории репозитория
Простое удаление файла и новый коммит не решают проблему, потому что секрет остаётся в истории. Автор использовал git-filter-repo (по сравнению с BFG он даёт больше контроля). Команда, использованная для удаления секретов:
git clone --mirror https://github.com/owner/repo.git repo.git
cd repo.git
git filter-repo --path config/.env --invert-paths
# или удалить паттерны токенов
git filter-repo --replace-text replacements.txt
# push back
git push --force --all
git push --force --tags
Файл replacements.txt выглядел так (маскируя часть токена):
ghp_********************=>[REMOVED GITHUB TOKEN]
github_pat_********************=>[REMOVED GITHUB TOKEN]
Также был удалён файл из рефа, чтобы исключить появление токена в ветках. В процессе очистки было затронуто 4 старых коммита и 3 ветки, общий размер репозитория уменьшился на ~120 KB, что указывает на небольшое по объёму попадание секрета.
3) Ротация и ограничение прав
Кроме отзыва первоначального токена, автор создал новый токен с минимальными правами: только repo:read и дополнительные права ограничены. Были выполнены следующие действия:
- Отозван старый токен (2025-09-15 16:02 UTC).
- Создан новый token с более узкими правами (выдан 2025-09-15 16:20 UTC) и заменён в CI-переменных через GitHub Secrets.
- Для доступа к сторонним сервисам внедрены краткоживущие (ephemeral) ключи там, где это возможно.
4) Обновление процессов разработки
Чтобы снизить вероятность повторного попадания секретов в репозиторий, внедрили следующие меры:
- Добавлен
.envв.gitignoreи обновлёнREADME.mdс инструкцией по локальной конфигурации; - Добавлен pre-commit хук на основе
pre-commitиgitleaksв качестве проверки; - В CI добавлена проверка на утечки: запуск gitleaks при открытии PR;
- Включено серверное сканирование секретов в настройках GitHub (secret scanning) для организации.
Пример pre-commit конфигурации, добавленной в .pre-commit-config.yaml:
- repo: https://github.com/zricethezav/gitleaks
rev: v8.2.0
hooks:
- id: gitleaks
5) Валидация исправлений
После очистки истории я повторно прогнал сканирование на своей стороне и использовал GitHub Security features. Отчёт 2025-09-22 показал отсутствие совпадений по первоначальному шаблону и наличие новых pre-commit проверок. Автор прислал подтверждение и ссылки на публичный changelog, где было отмечено исправление (запись 2025-09-20: "Removed leaked token, added pre-commit checks, rotated credentials").
Что узнал
Опыт показал, что даже у аккуратных проектов человеческий фактор остаётся главным источником ошибок: merge веток, перенос локальных настроек и отсутствие предзапусковой проверки приводят к утечке секретов. Ниже — список практических выводов и рекомендаций, которые сработали в этом случае.
- Регулярно сканируйте репозиторий и историю на наличие секретов: используйте gitleaks, truffleHog, git-secrets и интеграции GitHub Secret Scanning.
- Добавьте pre-commit хуки и проверку на PR уровнях: предотвращение полезнее лечения.
- Используйте короткоживущие токены и минимальные права (principle of least privilege).
- Если секрет попадал в историю — обязательная очистка: git-filter-repo или BFG, затем форс-пуш и уведомление пользователей о перебазировании веток.
- Включите секреты в настройки организации на GitHub и используйте централизованные хранилища (GitHub Secrets, HashiCorp Vault, AWS Secrets Manager).
Технические рекомендации
- Поиск в истории:
git log -S "github_pat_" -p --all - Удаление с помощью git-filter-repo:
git filter-repo --replace-text replacements.txt - Добавление проверки в CI (пример для GitHub Actions):
name: Secret scan on: [pull_request] jobs: scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Run gitleaks uses: zricethezav/gitleaks-action@v1
Организационные выводы
Совокупность технических мер должна сочетаться с политиками и коммуникацией внутри команды. Советую внедрить следующие процессы:
- Обучение разработчиков: краткие инструкции по работе с конфигурациями — как и где хранить секреты локально.
- Интеграция security champion в команды: ответственный за безопасность, который проверяет изменения и следит за интеграциями.
- Рутинная ротация ключей и автоматическое аннулирование при уволении сотрудников или смене обязанностей.
Результатом принятых мер в рассмотренном случае стало снижение риска и исправление инцидента в пределах одной рабочей недели: начальная находка — 2025-09-14, завершение очистки и валидация — 2025-09-22. В течение этого времени были предприняты отслеживаемые шаги и получена обратная связь от автора.
Небольшая оплошность в виде одного файла может привести к большему риску, если секрет имеет широкие права. Быстрая реакция и планирование процессов — ключ к минимизации ущерба.
Если хотите протестировать свои репозитории, начните с раздела про безопасность и обзора практик в департаменте DevOps. Небольшая автоматизация на CI и несколько простых правил в .gitignore спасают от большинства банальных утечек.
Заключение: утечка секретов github — это частая и решаемая проблема, но только при наличии правильных инструментов и культуры разработки. Быстрое обнаружение, прозрачная коммуникация с владельцами и корректная очистка истории позволяют минимизировать последствия и снизить вероятность повторного инцидента.
Комментарии (0)
Войдите или зарегистрируйтесь, чтобы оставить комментарий
Загрузка комментариев…