Безопасное хранение и доступ к API‑ключам в React

Современные веб‑приложения часто обращаются к внешним API для расширения функциональности. Многие API используют идентификаторы — ключи и секреты — чтобы связывать запросы с конкретным приложением. Эти ключи чувствительны: если вы «залите» их в репозиторий на GitHub, кто‑угодно сможет отправлять запросы к API от вашего имени. В этой статье показано, как безопасно хранить и использовать API‑ключи в React‑приложении.
Основная идея
Определение в одну строку: переменные окружения — это способ вынести конфигурацию вне кода; секреты должны храниться на сервере, а фронтенд должен получать только то, что можно безопасно раскрывать пользователю.
Добавление переменных окружения в приложение CRA
React‑приложение, созданное с помощью create‑react‑app, поддерживает переменные окружения «из коробки». CRA подхватывает переменные, которые начинаются с REACT_APP, и делает их доступными через process.env в сборке веб‑приложения. Это достигается за счёт использования пакета dotenv во время сборки.
- В корне проекта создайте файл .env
- Добавляйте публичные (не чувствительные) значения с префиксом REACT_APP
Пример содержимого .env:
REACT_APP_API_KEY="your_api_key"Доступ к переменной из любого файла в React‑приложении:
const API_KEY = process.env.REACT_APP_API_KEYВажно: добавьте .env в .gitignore, чтобы git не отслеживал этот файл.
Почему нельзя хранить секретные ключи в .env фронтенда
Файлы .env используются только на этапе сборки. Всё, что попадает в сборку (bundle), становится доступно в итоговых статических файлах. Злоумышленник может просмотреть исходники сборки или сетевые запросы и найти в них ключи. Поэтому секреты нельзя считать конфиденциальными, если они встроены в клиентский код.
Решение: вынести работу с секретами на сервер и сделать так, чтобы клиент обращался к вашему бэкенду без прямого знания секретного ключа.
Хранение переменных окружения в бэкенд‑коде
Создайте отдельное серверное приложение (например, Node.js/Express), где будут храниться секретные переменные в окружении сервера или в безопасном хранилище (см. разделы ниже).
Пример простого endpoint, который использует переменную окружения на сервере:
const apiURL = process.env.API_URL
app.get('/data', async (req, res) => {
const response = await fetch(apiURL)
const data = response.json()
res.json({data})
})Фронтенд вызывает только ваш сервер:
const data = await fetch('http://backend-url/data')Даже если .env не попадёт в GitHub, секреты останутся на сервере и не будут включены в статические файлы клиента.
Использование Next.js для скрытия секретов
Next.js разрешает доступ к приватным переменным окружения на серверной стороне, например внутри getStaticProps или API‑роутов. Код, выполняющийся в getStaticProps, запускается во время сборки на сервере, поэтому переменные окружения доступны только в Node.js‑контексте.
Пример getStaticProps:
export async function getStaticProps() {
const res = await fetch(process.env.API_URL)
const data = res.json()
return {props: { data }}
}Компонент получает данные через props:
function Home({ data }) {
return (
// render data
);
}В Next.js нет необходимости добавлять префикс REACT_APP — просто укажите переменную в .env:
API_URL=https://secret-url/de3ed3fNext.js также позволяет создавать API‑роуты в папке pages/api. Код внутри этих роутов выполняется на сервере и может обращаться к секретам, не раскрывая их клиенту.
Пример API‑роута pages/api/getData.js:
export default async function handler(req, res) {
const response = await fetch(process.env.API_URL)
const data = response.json()
return res.json({data})
}Фронтенд может вызывать /api/getData, не зная API_URL.
Практические рекомендации по безопасности
- Никогда не включайте секретные ключи в клиентскую сборку.
- Используйте бэкенд‑прокси: клиент запрашивает ваш сервер, сервер делает вызов к третьему API с секретом.
- Для инфраструктуры используйте менеджеры секретов (HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, Google Secret Manager) вместо хранения секретов в файлах .env на серверах.
- Ограничьте права API‑ключей: выдавайте ключи с минимально необходимыми правами и сроком жизни, если возможно.
- Логируйте и отслеживайте необычные запросы для быстрой реакции на компрометацию ключей.
- Ротация ключей: имейте процесс смены ключей и инвалидирования старых.
Важно: .env на локальной машине — это удобно для разработки, но не безопасно для продакшена.
Альтернативные подходы
- Secrets manager (рекомендовано для продакшна): хранит и версионирует секреты, упрощает ротацию и выдаёт доступ через IAM/ролей.
- Encrypted configuration в CI/CD: храните зашифрованные переменные в системе CI и подставляйте их при деплое.
- Short‑lived credentials: использовать временные токены, которые истекают через короткое время.
Когда предложенный подход не работает
- Вы используете статический хостинг без бэкенда и не можете добавить прокси — тогда придётся использовать сторонний сервис, который предоставляет безопасный клиентский ключ с минимальными правами или реализовывать серверную прослойку.
- Потребность в крайне низкой латентности между пользователем и внешним API при невозможности вызвать ваш сервер — в таких случаях рассмотрите выдачу ограниченных прав ключа или кеширование результатов на сервере.
Ментальные модели и эвристики
- Разделяй и властвуй: клиент = интерфейс, сервер = доверенная зона для секретов.
- Минимизация поверхности атаки: раскрывайте клиенту только те данные, которые ему действительно нужны.
- Последовательность доверия: окружение разработчика < CI < staging < production — по мере продвижения увеличивайте уровень защиты переменных.
Факт‑бокс
- Переменные с префиксом REACT_APP автоматически встраиваются в сборку CRA.
- Код, выполняющийся на сервере (getStaticProps, API‑роуты Next.js), может безопасно читать секреты из окружения.
- Для продакшн‑запусков лучше использовать менеджеры секретов и IAM.
Мини‑методология (шаг за шагом)
- Выделите секреты: какие ключи нельзя публиковать.
- Уберите все секреты из фронтенда и замените их вызовами к вашему бэкенду.
- Настройте хранение секретов на сервере через менеджер секретов или защищённые переменные окружения CI/CD.
- Реализуйте прокси‑endpoint, который работает с внешним API и возвращает клиенту только нужные данные.
- Настройте мониторинг, логирование и ротацию ключей.
Ролевые чек‑листы
Frontend developer
- Удалить секреты из репозитория.
- Заменить прямые вызовы к внешнему API на вызовы к вашему API.
- Обновить README с инструкцией по локальной разработке (.env.example).
Backend developer / DevOps
- Настроить хранение секретов в менеджере секретов или в защищённых переменных окружения CI.
- Реализовать прокси‑эндпоинты с контролем доступа.
- Обеспечить логирование и ротацию ключей.
Security engineer
- Провести аудит прав ключей и выдать минимальные привилегии.
- Настроить мониторинг аномалий использования ключей.
Критерии приёмки
- В продакшн‑сборке нет секретных ключей в статических файлах.
- Все чувствительные вызовы идут через проверенные серверные эндпоинты.
- Доступ к секретам реализован через менеджер секретов или защищённые переменные окружения.
- Реализована процедура ротации ключей и мониторинга.
Тесты и критерии приемки
- Инструментальный тест: скан сборки на наличие известных паттернов ключей (должен вернуть пустой результат).
- Интеграционный тест: фронтенд получает данные только от вашего бэкенда, бэкенд успешно запрашивает внешний сервис используя секрет из окружения.
- E2E: сценарий с истёкшим ключом — сервер должен корректно обрабатывать ошибки и уведомлять команду.
Переезд и совместимость
- При миграции с CRA на Next.js перенесите секреты в серверные функции Next.js или в менеджер секретов. Убедитесь, что клиентские переменные REACT_APP заменены на безопасные вызовы к API.
- При переходе на контейнеры используйте секреты платформы (Kubernetes Secrets, Docker Secrets) вместо файлов .env в контейнере.
Риск‑матрица и смягчение
- Утечка ключа через репозиторий: немедленная ротация ключей, анализ инцидента.
- Неправильные права ключа: ограничить scope/permissions, внедрить least privilege.
- Логирование секретов: запретить запись секретов в журналы и трассировки.
Короткое объявление для команды
Добавьте в README проекта инструкцию: для локальной разработки используйте .env.local (в .gitignore), для продакшна храните секреты в менеджере секретов и обеспечьте вызов внешних API через сервер‑прокси.
Итого: используйте переменные окружения в CRA только для публичной конфигурации. Все секреты держите на сервере или в специализированных хранилищах, реализуйте прокси‑эндпоинты и мониторинг, и обязательно планируйте ротацию ключей.
Похожие материалы
Как копировать формулы в Excel эффективно
Фокус на iPad — настройка и лучшие практики
Защитить домашний Wi‑Fi: как не дать соседям воровать интернет
Профессиональный баннер LinkedIn в Canva
Spotify Wrapped 2024 — как посмотреть и что нового