RussiaAPI

Безопасность веб-интеграции

CORS и OpenAI-совместимый API: почему ключ должен остаться на сервере

Запрос из браузера к OpenAI-совместимому API часто упирается в CORS. Правильная реакция — не ослаблять защиту и не вставлять секрет в JavaScript, а построить собственный server-side маршрут. RussiaAPI — независимый сторонний gateway: сначала проверьте актуальный контракт и модель в каталоге, затем выдайте браузеру только результат, допустимый для текущего пользователя.

Опубликовано 1 сентября 2026 · 10 минут чтения · Ключевой запрос: CORS OpenAI-совместимый API ключ на сервере

Граница сервиса. RussiaAPI — независимый сторонний API gateway, а не официальный сервис OpenAI, Anthropic, Google или производителя модели. OpenAI-совместимый формат означает только проверяемый контракт запроса и не гарантирует одинаковые модели, функции, цены, доступность, правила или хранение данных. Используйте только собственный RUSSIAAPI_API_KEY на сервере; не передавайте внешние ключи, cookie, пароли, коды подтверждения или лишние персональные данные.

Что CORS действительно означает

CORS — это правило браузера, которое ограничивает доступ страницы одного origin к ответам другого origin. Оно защищает пользователя от незаметного чтения данных чужим сайтом, но не является средством аутентификации вашего API. Если frontend видит ошибку CORS, это не доказательство, что endpoint «сломался» или что нужно открыть его для любого домена. Сначала отделите браузерный сценарий от server-to-server вызова: backend не подчиняется CORS, а браузер подчиняется.

У API-ключа другая задача: он подтверждает доступ приложения к сервису. Если поместить его в bundle, localStorage, мобильный клиент или расширение браузера, пользователь и любой скрипт на странице смогут его извлечь. Даже короткоживущий ключ не превращается в безопасный публичный идентификатор автоматически. Поэтому браузер не должен вызывать RussiaAPI с Authorization; он обращается к вашему приложению, а ваш сервер хранит собственный секрет и применяет бизнес-правила.

Спроектируйте границу между frontend и backend

Создайте маршрут вроде POST /api/assistant в своём приложении. Он проверяет сессию пользователя, tenant, право на конкретную функцию, размер входа и допустимый сценарий. Только после этого сервер формирует минимальный payload, выбирает заранее разрешённый model ID и вызывает API с ключом из secret manager или переменной окружения. Клиент получает текст, структурированный результат или свой ID задачи, но никогда не получает полный ответ поставщика, заголовки и внутренние маршруты.

Хорошая граница также задаёт лимиты. Ограничьте размер сообщения, число одновременных операций и дневной бюджет конкретного продукта; для долгих видео-задач возвращайте внутренний task ID. Не полагайтесь на CORS как на контроль доступа: origin можно подделать вне браузера, а запросы к вашему backend всё равно нуждаются в аутентификации и авторизации. Внутренний маршрут должен логировать безопасный request ID, класс статуса и задержку, но не prompt, Bearer-токен или персональные поля.

Настройте origin строго и осмысленно

Если frontend и backend находятся на разных origin, разрешите только известные production- и test-домены. Не используйте * вместе с cookie или учётными данными и не отражайте любой пришедший Origin без allowlist. Для обычного JSON POST браузер может отправить preflight OPTIONS; ответьте только разрешёнными методом, заголовками и origin. Список origin храните как конфигурацию окружения, а не как строку, которую пользователь может передать в запросе.

Учитывайте, что CORS не исправляет ошибку сессии. Если запрос приходит без корректной пользовательской сессии, backend должен вернуть понятный 401 или 403, не раскрывая состояние ключа RussiaAPI. Если payload неверный, верните собственную ошибку валидации, а не сырой ответ внешней системы. Такой контракт проще тестировать и он не заставляет frontend угадывать особенности каждой модели. Общую диагностику прав и окружения дополняет руководство по ошибкам 401 и 403.

Минимальный пример server-side маршрута

Ниже пример для Node.js 18+. Он предполагает, что requireUser проверяет сессию, а rateLimit ограничивает ваш продукт. Код не является браузерным примером: переменная с ключом доступна только процессу сервера. Перед запуском проверьте текущий model ID и допустимые поля по каталогу и документации RussiaAPI.

export async function postAssistant(request) {
  const user = await requireUser(request); // your session and tenant check
  const input = await request.json();
  if (typeof input.message !== 'string' || input.message.length > 4_000) {
    return Response.json({ error: 'invalid_message' }, { status: 400 });
  }
  await rateLimit(user.id, 'assistant');
  const res = await fetch('https://russiaapi.com/v1/chat/completions', {
    method: 'POST',
    headers: { authorization: `Bearer ${process.env.RUSSIAAPI_API_KEY}`, 'content-type': 'application/json' },
    body: JSON.stringify({ model: process.env.RUSSIAAPI_TEXT_MODEL, messages: [{ role: 'user', content: input.message }] }),
    signal: AbortSignal.timeout(12_000)
  });
  if (!res.ok) return Response.json({ error: 'assistant_unavailable' }, { status: 503 });
  const data = await res.json();
  return Response.json({ text: data.choices?.[0]?.message?.content ?? '' });
}

В production добавьте схему входа, ограничение длины и безопасный обработчик ошибок. Не возвращайте клиенту res.text() при неуспехе: в нём могут быть детали, не предназначенные для пользователя. Для streaming используйте свой авторизованный поток и корректно завершайте его при отключении клиента; базовый подход описан в материале об SSE streaming.

Разберите частые ошибочные решения

Первое ошибочное решение — отключить CORS на любом origin и оставить ключ во frontend. Это одновременно создаёт утечку и лишает вас возможности ограничить сценарий. Второе — проксировать весь внешний API без проверки пути, метода и тела. Такой «универсальный» proxy позволяет клиенту расходовать бюджет непредсказуемо и усложняет аудит. Третье — просить пользователя прислать ключ или полный сетевой лог в поддержку. Для обычной диагностики достаточно времени, URL вашего маршрута, HTTP-класса и внутреннего request ID.

Не превращайте ошибку CORS в инструкцию по обходу ограничений платформ или региональных правил. Если функция недоступна, покажите честный статус, проверьте собственную конфигурацию и действующие условия сервиса. RussiaAPI не следует описывать как официальный endpoint какого-либо поставщика. Совместимый запрос — это повод сделать контрактный тест, а не обещание полной функциональной идентичности. Для первого безопасного smoke test используйте пошаговое подключение совместимого API.

Чек-лист перед выпуском веб-функции

  1. Ключ RussiaAPI есть только в server-side secret, а не в bundle, localStorage или журнале браузера.
  2. Маршрут проверяет сессию, права, размер входа и лимит вашего продукта до обращения к модели.
  3. CORS allowlist содержит только контролируемые origin и не отражает произвольный Origin.
  4. Model ID и параметры подтверждены текущим каталогом, а не старой конфигурацией.
  5. Логи содержат только безопасные метаданные; ошибка пользователю не раскрывает секреты или ответ поставщика.

После релиза проведите тест с обычной сессией, чужим tenant, запрещённым origin и некорректным JSON. Это проверяет вашу границу лучше, чем один успешный запрос. Если меняется домен, способ входа или модель, повторите контрактный тест отдельно, а затем включайте трафик постепенно.

Проверьте сценарий в RussiaAPI

Создайте собственный тестовый ключ в консоли, сверьте текущий каталог моделей и начните с обезличенного server-side smoke test. Расширяйте доступ, трафик и бюджет только после измеримой проверки.

Открыть консоль RussiaAPI · Документы · Каталог моделей

FAQ

Нужно ли разрешать CORS для RussiaAPI в браузере?

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

Можно ли хранить API key в localStorage?

Нет. Любой скрипт на странице или пользователь браузера может извлечь его. Храните собственный ключ RussiaAPI только в server-side secret и возвращайте клиенту лишь результат операции.

Что логировать при CORS-ошибке?

Зафиксируйте свой маршрут, время, HTTP-класс и внутренний request ID. Не добавляйте Authorization, cookie, полный prompt, ответ модели или персональные данные в браузерные и серверные журналы.

Читайте также