Если браузеру нужен секретный credential для прямого вызова приватного API, этот credential станет доступен посетителям через JavaScript или сетевой запрос. Некоторые сервисы выдают публичные идентификаторы или ключи с ограничениями; они не заменяют секрет и не должны автоматически называться секретами. Проверь назначение значения в документации. Секрет провайдера храни на сервере, а backend пусть проверяет пользователя и допустимую операцию.
OpenAI рекомендует защищённое хранение ключей, ограничения доступа, мониторинг и ротацию; отдельно запрещает делиться одним ключом между участниками и советует отдельные ключи и срок действия (OpenAI API key safety). GitHub secret scanning помогает находить секреты в истории репозитория, но сканирование — дополнительный слой, а не место для хранения ключей (GitHub docs).
Что можно публиковать, а что нет
Публичная конфигурация — например, адрес backend, публичный идентификатор аналитики или флаг интерфейса. Она может быть видна в собранном JS.
Секрет — API key, bearer token, приватный signing key, пароль, webhook secret или credential, который позволяет совершить действие от имени владельца. Его нельзя класть в frontend-переменные и считать скрытым из-за минификации.
В Vite переменные с префиксом VITE_ доступны клиентскому коду; Next.js встраивает используемые NEXT_PUBLIC_ значения в браузерную сборку. Название вроде VITE_SECRET не делает значение секретным. Правила конкретного сборщика нужно проверять по документации и итоговому артефакту. Переменные Vite, переменные Next.js.
Минимальная схема
Браузер
└─ POST /api/summary { text: ... }
└─ Сервер проверяет пользователя, размер и лимит
└─ Сервер читает API_KEY из secret storage
└─ Вызов провайдера
На сервере добавь аутентификацию, лимит размера, rate limit, проверку допустимой операции и управление расходами. Не возвращай клиенту ключ в ответе или ошибке. В логах не сохраняй заголовки авторизации, полный prompt с персональными данными или секретные конфигурации.
Проверь результат сборки
- Собери production-версию.
- Найди в
dist/, сжатых JS-файлах и source maps названия и фрагменты секретных значений. - Проверь сетевые запросы в браузере: нет ли ключа в URL, заголовке клиента или теле ответа.
- Проверь репозиторий и историю коммитов secret scanning инструментом.
- Убедись, что frontend вызывает только твой ограниченный endpoint.
Это контрольный список, не аудит конкретного проекта. Не вставляй настоящий секрет в тестовый пример.
Если ключ уже попал в клиент или Git
Считай его раскрытым, даже если удалил строку или переписал историю. Сначала отзови ключ у провайдера; затем проверь активность и выставленные лимиты, выпусти новый в безопасном хранилище, обнови сервер и проверь приложение. Если в репозиторий попал один ключ для нескольких сред — раздели их и обнови каждую. Удаление следа из истории снижает дальнейшее распространение, но не возвращает секретность уже опубликованному значению.
Раскрытый credential может остаться в source maps, истории Git, CI-логах, crash-reporting, скриншотах или URL. Удали доступные копии и не публикуй полный лог хостинга. Пользовательский токен для client-side auth отличается от секрета провайдера: он должен быть короткоживущим и иметь минимальный scope.
Секретное хранилище не решает проблему, если сервер принимает любой запрос анонимно и проксирует его с твоим ключом. Проверь пользовательскую авторизацию, лимит расходов, rate limit, допустимые модели/операции и ограничения на длину ввода. Иначе backend превратится в публичный прокси за твой счёт.
После ротации проверь usage у провайдера, обнови все среды и убедись, что старый ключ больше не принимается. В отчёте зафиксируй дату и перечень обновлённых сред, но не само значение.
Разделяй среды и владельцев
Не используй один credential для локальной разработки, preview и production. Отдельные ключи помогают ограничить последствия утечки и понять, откуда пришёл запрос. Выдай каждому серверному компоненту только те права, которые ему нужны, и отключай неиспользуемые credentials. OpenAI рекомендует уникальные ключи для участников и управляемую ротацию; это особенно важно, когда проект перестаёт быть личным экспериментом.
Клиентский токен пользователя и секрет провайдера — разные вещи. Пользователь может авторизоваться в приложении, а backend — хранить отдельный server-to-server credential. Проверяй, что backend связывает пользователя с разрешённым объектом, а не просто добавляет провайдерский ключ к любому запросу. Интеграцию по токенам и scopes отдельно рассматривает безопасное подключение MCP.
Храни журнал ротации без значений ключей: дата отзыва, обновлённые среды и проверенный deployment.
Для любого оставшегося credential выбери минимальный scope, лимит расходов и понятный способ отзыва. После изменения сборки снова просканируй клиентский артефакт. Для разработки backend и интеграций смотри MK Ultra.
