Технологии

Что делать если ключ утёк: порядок действий

Разбор реального случая перед публикацией репозитория. Главный вывод — удаление из репозитория не отменяет утечку, отменяет её только отзыв ключа. Плюс чек-лист мест, где копия могла остаться.

Репозиторий Persona готовился к публикации. Перед этим шагом полагается проверить историю на секреты — то, что до сих пор было личным проектом на личной машине, становится доступным всем, и вместе с текущим состоянием кода открывается вся его история.

Проверка нашла не то, что ожидалось. История коммитов оказалась чистой. А в рабочем каталоге, в обычном рабочем документе передачи дел, лежал живой тестовый пароль и подробное описание сетевой топологии: какие туннели подняты, к каким машинам, под какими именами.

Ниже — что именно делалось, в каком порядке, что было сделано неправильно и какой из этого получился регламент.

#Хронология

Отметки времени — не хронометраж происшествия, а бюджет, которого стоит держаться. Порядок шагов важнее их длительности.

Минуты Действие Почему именно здесь
0–3 Отозвать или сменить сам секрет у поставщика единственное действие, которое реально отменяет утечку
3–5 Проверить, что новый секрет работает, а старый — нет иначе вы не знаете, отозвали ли вообще
5–15 Найти все места, где лежит копия список ниже; их всегда больше, чем кажется
15–30 Проверить журналы доступа у поставщика было ли использование, откуда, когда
30–60 Убрать секрет из репозитория и документов и только теперь
позже Поставить защиту, чтобы это не повторилось хук, тест, игнор-правила

Самое важное здесь — порядок первой и предпоследней строки. Интуиция требует сначала «убрать», а потом разбираться. Интуиция ошибается.

#Что мы сделали неправильно

Считали проверенной только историю git. Аудит гонялся по коммитам, и он был чистым — все 568 штук на тот момент. При этом секрет лежал в файле, который в этих же коммитах и находился, просто вне тех шаблонов, которые проверялись. Проверять надо и историю, и текущее дерево, и делать это разными проходами.

Держали в репозитории документ, который никто не считал секретным. Файл передачи дел — это по определению место, куда пишут «как всё устроено»: адреса, имена машин, топология туннелей, а в нашем случае и пароль от тестового стенда. Никто не воспринимает такой файл как хранилище секретов, потому что он «просто заметки». Это и есть механизм утечки: секрет попадает не в конфиг, а в прозу.

Тестовый пароль считали не настоящим. Он был настоящим: он открывал реальный стенд, поднятый на реальной машине, к которой был доступ снаружи. Слово «тестовый» в имени переменной не меняет того, что этим значением можно воспользоваться.

Описание топологии не считали чувствительным вообще. Пароль хотя бы очевиден. Описание того, какие туннели куда подняты и как называются машины, — это карта, которая экономит нападающему всю разведку. Утечка знания об инфраструктуре не выглядит как утечка и потому не чинится.

#Что сработало

Отзыв раньше уборки. Значение перестало быть валидным до того, как кто-либо начал переписывать файлы. Пока секрет действителен, всё остальное — косметика.

Полный проход по дереву, а не по последним коммитам. Проверка одной и той же логикой в двух режимах: по подготовленным к коммиту изменениям и по всему отслеживаемому дереву. Первое быстро и на каждый коммит, второе медленно и на каждый прогон тестов.

Расширение игнор-правил с имён на шаблоны. Было перечисление конкретных файлов, стало перечисление классов: .env*, *.db, *.pem, *.session, логи. Именованное правило защищает от уже известного файла; шаблон — от того, который появится завтра.

Заполненный шаблон переменных окружения. Каждая секретная переменная, встречающаяся в коде, документирована в шаблоне с пустым значением. Недокументированная переменная — прямая дорога к захардкоженному ключу: человек не нашёл, куда класть значение, и вписал его в код.

Как это ставится технически — хук, тест, набор шаблонов поиска — в статье про секреты в проекте и защиту от коммита.

#Где ещё могла остаться копия

Это главная часть чек-листа и самая недооценённая. Секрет копируется гораздо охотнее, чем кажется.

  • История команд оболочки. ~/.bash_history, ~/.zsh_history, журнал PowerShell (ConsoleHost_history.txt). Любой ключ, который вы хоть раз вставили в командную строку, лежит там открытым текстом.
  • Переменные окружения запущенных процессов. Перезапустите то, что читало старое значение, иначе процесс продолжит им пользоваться.
  • Файлы конфигурации инструментов. ~/.netrc, ~/.npmrc, ~/.pypirc, конфиги CLI облачных провайдеров.
  • Резервные копии рабочей машины. Копия репозитория месячной давности содержит секрет в прежнем виде.
  • Экспорт базы приложения. Если ключи лежат в базе — а в Persona ключ участника лежит в таблице настроек открытым текстом, — то любой полный экспорт базы это копия всех ключей. Обращаться с таким файлом надо как с хранилищем паролей и никогда не прикладывать его к обращению в поддержку.
  • Журналы приложения и веб-сервера. Тело запроса, попавшее в лог, содержит всё, что в нём было.
  • Переписка. Секрет, отправленный себе в мессенджер «чтобы скопировать на сервер», живёт там вечно.
  • Кэш систем сборки и артефакты CI. Переменные сборки печатаются в журналы чаще, чем хотелось бы.
  • Форки и клоны репозитория. Переписывание истории в вашей копии не трогает чужие.

Пройдитесь по списку целиком, а не по трём очевидным пунктам. Практика показывает: находится минимум одно место, о котором не подумали.

#Выводы и регламент

Регламент на будущее уместился в семь строк.

  1. Считать утёкшим всё, что попало в коммит, — даже локальный, даже на секунду. Ротация дешева, разбирательство после публикации — нет.
  2. Отзывать до уборки. Всегда.
  3. Проверять историю и дерево разными проходами, одним и тем же набором правил.
  4. Держать документы передачи дел под тем же режимом, что и код. В них секретов оказывается больше, чем в конфигах.
  5. Считать описание инфраструктуры чувствительным. Имена машин, адреса, топология туннелей — это не документация, это разведданные.
  6. Ставить защиту до ошибки. Хук плюс тест, который работает даже там, где хук не установили.
  7. После инцидента — посмотреть журналы доступа у поставщика. Отзыв закрывает будущее, журнал отвечает на вопрос про прошлое.

Отдельный пункт, который стоит держать в голове при выборе архитектуры: чем больше секретов у вас на руках, тем дороже такой инцидент. Ассистент, работающий на локальной модели, вообще не требует ключа провайдера — это не панацея, но это на один класс секретов меньше. Разбор того, какие угрозы при этом остаются, — в статье про угрозы локального ИИ. Где хранить ключ, если он всё-таки нужен, — в разборе безопасного хранения API-ключа.

#Частые вопросы

#Помогает ли удалить файл новым коммитом?

Нет. Старый объект остаётся достижимым в истории, и его находят автоматические сканеры репозиториев. Помогает переписывание истории — если ветка ещё никуда не отправлена. Если отправлена, считайте секрет опубликованным и отзывайте его.

#Что делать, если утёк ключ облачного провайдера модели?

Отзовите его в кабинете провайдера в первую очередь — там же обычно видно потребление, по которому понятно, воспользовался ли им кто-то. Дальше — новый ключ, обновление в настройках приложения и проход по чек-листу мест, где могла остаться копия. Учтите, что пока ключ был валиден, счёт мог расти на чужих запросах.

#Нужно ли переписывать историю, если репозиторий закрытый?

Если он останется закрытым навсегда — формально нет, но вы этого не знаете заранее. Репозиторий Persona был закрытым, и именно подготовка к возможной публикации заставила проверить историю. Проще один раз переписать, пока веток мало, чем разбираться потом.

#Как понять, воспользовался ли кто-то утёкшим ключом?

По журналам доступа со стороны поставщика: время запросов, адреса, объём. Собственные логи здесь помогают мало, потому что чужое использование ключа идёт мимо вашей машины. Если журналов у поставщика нет — исходите из худшего.

#Что делать пользователю сервиса, если утечка случилась не у него?

Сменить пароль, отозвать выданные токены и, если сервис это позволяет, потребовать удаления своих данных. Порядок действий и что именно удаляется — в статье про удаление аккаунта и данных.

#Стоит ли из-за таких историй отказаться от самостоятельного хостинга?

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

Хотите ИИ, который помнит вас?

Persona держит контекст между чатами и работает на вашем железе — если развернуть её у себя и подключить локальную модель.

Создать аккаунт