Приватность

Где хранить API-ключ и как понять что он утёк

Уровни защиты ключа от переменной окружения до аппаратного хранилища, признаки утечки и порядок замены. Включая честный разбор того, как ключи хранит Persona.

API-ключ устроен максимально неудобно с точки зрения безопасности: это строка без пароля, без второго фактора и без привязки к устройству. У кого строка — тот и тратит ваши деньги, причём с любого адреса и в любое время. Никакого «подтвердите вход» не будет.

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

#Как ключи утекают на самом деле

Не через взлом провайдера. Обычные каналы скучнее и оттого распространённее:

  • Репозиторий. Ключ попал в файл конфигурации, файл попал в коммит. Публичные репозитории сканируются автоматически — счёт идёт на минуты, а не на дни. История коммитов при этом хранит ключ и после того, как вы его «удалили» из текущей версии файла.
  • Скриншот и переписка. Ключ в кадре при демонстрации экрана, в чате поддержки, в тикете.
  • История команд оболочки. Ключ, переданный аргументом команды, остаётся в истории и в списке процессов.
  • Чужое приложение. Вы вставили ключ в клиент, о котором знаете только название. С этого момента ключ у его автора.
  • Резервная копия базы. Если приложение хранит ключ в открытом виде, ключ есть в каждом бэкапе, включая тот, что лежит в облаке.
  • Логи. Ключ, попавший в отладочный вывод, живёт в логах ровно столько, сколько живут логи.

#Уровни защиты: от худшего к лучшему

Смотрите на последнюю колонку: она отвечает на вопрос, от чего конкретно защищает уровень. «Безопасно вообще» не бывает.

Уровень Где лежит ключ От чего защищает
0 в коде или в конфиге репозитория ни от чего
1 в файле .env вне репозитория от случайного коммита
2 в переменной окружения процесса от чтения файла проекта
3 в менеджере паролей, вставляется руками от кражи с диска сервера
4 в зашифрованном хранилище приложения от чтения базы без пароля
5 в системном хранилище секретов от доступа других процессов

Уровень 1 — минимум, ниже которого опускаться нельзя ни при каких обстоятельствах. Как настроить его так, чтобы он работал не на честном слове, разобрано в материале про секреты в .env и git-хуки.

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

#Как хранит ключи Persona — без прикрас

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

Ключи участников хранятся в базе в открытом виде. Когда участник подключает своего провайдера, строка вида byo_api_key_<провайдер> пишется в таблицу пользовательских настроек без шифрования. Это уровень 1 из таблицы выше — с оговоркой, что защищает он только от коммита, но не от того, у кого есть доступ к файлу базы или к её резервной копии.

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

Что из этого следует для вас, по пунктам:

  1. Выпускайте для Persona отдельный ключ, а не тот, которым пользуетесь в других приложениях.
  2. Ставьте на этот ключ лимит трат в кабинете провайдера, если провайдер это позволяет.
  3. Если разворачиваете Persona у себя — относитесь к файлу базы как к файлу с секретами: ограничивайте права доступа и шифруйте резервные копии.
  4. Если пользуетесь размещённой версией — ключ лежит на её сервере, и это нужно учитывать при выборе, какой ключ туда вставлять. На размещённой версии, кроме того, стоит Яндекс.Метрика с вебвизором, который пишет клики и навигацию по страницам; при развёртывании у себя счётчика нет.

Мы не можем предложить вам аргумент «проверьте код»: репозиторий закрыт, публичной лицензии нет. Это осознанное ограничение позиции, и оно вместе с другими слабыми местами разобрано в материале о том, почему мы проигрываем по некоторым пунктам.

#Признаки утечки

Ключ не сообщает о том, что им пользуется кто-то ещё. Замечают это по косвенным признакам:

  • Расход вырос без изменения вашей активности. Самый надёжный признак. Смотрите дневной график в кабинете провайдера, а не месячную сумму.
  • Запросы к моделям, которые вы не вызываете. Особенно к дорогим или к тем, что не поддерживаются вашим приложением.
  • Активность в часы, когда вы не работаете. Ровный поток ночью — почти всегда не вы.
  • Письмо от провайдера об обнаружении ключа в публичном источнике. Часть сервисов сканирует публичные репозитории и отзывает ключ сама.
  • Ошибка 401 без ваших действий. Иногда это уже отзыв ключа провайдером после обнаружения утечки.

#Процедура отзыва и замены

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

  1. Отзовите ключ в кабинете провайдера. Не «замените в приложении», а именно отзовите — иначе старый продолжает работать.
  2. Выпустите новый ключ с ограничением по лимиту трат.
  3. Замените его во всех местах, где использовался старый. Составьте список заранее: приложения, серверы, скрипты, переменные окружения на машинах разработки.
  4. Проверьте историю расхода за период с момента вероятной утечки. Это нужно и для оценки ущерба, и для обращения к провайдеру.
  5. Найдите канал утечки. Если ключ был в репозитории, помните: удаление из файла не удаляет его из истории коммитов. Порядок действий при утечке в репозиторий разобран в материале про ротацию утёкших учётных данных.
  6. Проверьте соседние секреты. Утечка редко бывает одиночной: если ключ уехал в коммит, вместе с ним обычно уехало что-то ещё.

#Проверка результата

  1. Старый ключ в кабинете провайдера значится отозванным.
  2. Запрос со старым ключом возвращает 401.
  3. Приложения работают на новом ключе, и в разделе использования виден расход именно по нему.
  4. Через сутки график расхода вернулся к вашей норме.

Четвёртый пункт — единственный, который подтверждает, что утечка закрыта. Если расход продолжается, значит утёк не только этот ключ.

#Что может пойти не так

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

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

#Можно ли считать хранение ключа в базе приложения приемлемым?

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

#Насколько защищает мастер-пароль приложения?

Он защищает содержимое хранилища от чтения без пароля: копия базы без пароля бесполезна. Он не защищает от компрометации работающего приложения, потому что во время работы ключ находится в памяти процесса в открытом виде. Это разные угрозы, и путать их не стоит.

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

Считайте его скомпрометированным немедленно, независимо от того, сколько он там пробыл: публичные репозитории сканируются автоматически. Отзовите ключ первым действием, до всякой чистки истории. Чистка истории без отзыва бесполезна.

#Как понять, какое приложение сливает ключ?

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

#Стоит ли шифровать резервные копии базы?

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

#Есть ли вариант, при котором ключ не нужен вовсе?

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

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

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

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