Короткий ответ
Нужно явно перечислить разрешённые origin для каждого типа клиента (а не использовать wildcard-разрешение всех origin), развести механизмы защиты по типу аутентификации (CSRF актуален для cookie-based сессий из браузера, но не для API-ключей/токенов мобильного приложения или сервер-сервер интеграции партнёра) и решить, где физически применяется эта политика — на уровне API Gateway единообразно или по-разному на каждом отдельном сервисе за ним.
Как это работает подробнее
CORS защищает браузер пользователя от того, чтобы чужой сайт мог от имени пользователя читать ответы вашего API через JS без явного разрешения — это актуально именно для браузерных клиентов (веб-фронтенд, виджет, встраиваемый в сторонние сайты). CSRF актуален, когда аутентификация идёт через cookie, автоматически прикладываемый браузером к любому запросу на ваш домен, включая инициированные сторонним сайтом; для мобильного приложения или серверной интеграции партнёра, где аутентификация — явный токен в заголовке, а не cookie, классический CSRF неприменим так же, как для браузера.
Единая политика на уровне API Gateway проще в поддержке и аудите, но может быть либо слишком строгой для части клиентов (блокирует легитимную интеграцию партнёра), либо слишком мягкой для другой части (излишне разрешает происхождение там, где не нужно). Индивидуальная настройка по сервису/эндпоинту точнее соответствует реальным требованиям безопасности каждого клиента, но увеличивает число мест, которые нужно поддерживать согласованно и не забыть при добавлении нового партнёра или эндпоинта. Критерий выбора — насколько разнородны клиенты и чувствительность конкретных операций, а не универсальное правило для всего API.
Пример кода
Политика (пример, не единственно верная):
- Веб-фронтенд (app.example.com), cookie-аутентификация:
CORS: allow-origin = https://app.example.com только; CSRF-токен обязателен для mutating-запросов.
- Партнёрский виджет, встраиваемый на сайты партнёров, без cookie:
CORS: allow-origin = явный список доменов партнёров из реестра интеграций;
аутентификация через API-ключ в заголовке, CSRF неприменим (нет cookie).
- Мобильное приложение, токен в заголовке Authorization:
CORS неприменим (не браузерный cross-origin сценарий); CSRF неприменим.
Любое добавление нового партнёрского домена проходит через явный реестр,
а не разрешается динамически по заголовку Origin без проверки.Что использовать на практике
- Практика. Партнёрский виджет, встраиваемый на сторонние сайты, обычно требует разрешить CORS с конкретных доменов партнёров (поддерживаемый и управляемый список, а не wildcard), в то время как мобильное приложение не делает браузерных cross-origin запросов вовсе и CORS к нему неприменим. Решение о применении политики на уровне Gateway (единообразно для всех сервисов) против индивидуальной настройки на каждом сервисе зависит от того, насколько разные сервисы за Gateway обслуживают разных клиентов с разными требованиями.
Подводные камни
- Риск. Включают
Access-Control-Allow-Origin: *«для удобства интеграции партнёров», что потенциально позволяет любому сайту делать запросы от имени залогиненного cookie-пользователя, если какой-то эндпоинт ошибочно использует cookie-аутентификацию вместе с таким разрешением; применяют одну и ту же политику CORS/CSRF ко всем эндпоинтам без учёта того, что часть из них — публичные данные без аутентификации, а часть — чувствительные операции от имени пользователя.