Кросс-доменные запросы давно перестали быть загадкой для веба, но ошибка в конфигурации может дорого обойтись. В этой статье разберём, как грамотно выполнить CORS настройка и безопасность, чтобы открывать доступ там, где это нужно, и закрывать — где нельзя.

Что такое CORS и зачем его настраивать

Cross-Origin Resource Sharing — это механизм браузера, который регулирует доступ одной страницы к ресурсам, расположенным на другом домене. Политика введена ради безопасности: без неё любой сайт мог бы бесконтрольно обращаться к API и читать ответы.

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

Как работает механизм CORS — простая модель

Браузер добавляет заголовок Origin в запрос, который показывает источник (схема, домен, порт). Сервер смотрит этот заголовок и решает, возвращать ли ответ вместе с Access-Control-Allow-Origin, указывающим разрешённый источник.

Для некоторых операций — нестандартных методов или нестандартных заголовков — браузер сначала отправляет preflight-запрос OPTIONS. Сервер в ответ должен вернуть набор разрешений, иначе основной запрос будет блокирован. Это простая и безопасная модель, если на стороне сервера выполняется внятная логика валидации происхождения запросов.

Типичные ошибки и реальные риски

Самая частая ошибка — установка Access-Control-Allow-Origin: * в API, которые принимают учётные данные или возвращают чувствительные данные. Такой универсальный доступ годится для статических ресурсов, но опасен при работе с cookie или токенами.

Другой распространённый промах — включение Allow-Credentials вместе с wildcard-оригином. Браузер отклонит такой ответ, но нередко это сопровождается попытками «обойти» политику на клиенте, что ведёт к уязвимостям. Также разработчики иногда разрешают слишком широкий набор методов и заголовков, что расширяет поверхность атаки.

Наконец, забывают про Vary: Origin. Без этой подсказки прокси и CDN могут кэшировать ответ с одним Origin и отдать его другому, что нарушит изоляцию. В сумме эти ошибки увеличивают риск кражи данных, подлогов запросов и обхода аутентификации.

Принципы безопасной конфигурации

Безопасность — это набор ограничений, а не их отсутствие. Лучше разрешать доступ по списку доверенных источников, чем открывать доступ всем подряд.

Ниже — практические правила, которые применимы к большинству сервисов:

  • Указывайте конкретные Origin вместо звёздочки, когда работа идёт с credentials (cookie, Authorization). Это снижает шанс непреднамеренного раскрытия.
  • Используйте Access-Control-Allow-Credentials только при реальной необходимости. Токены лучше передавать в заголовках Authorization, а cookie — с правильными флагами SameSite и Secure.
  • Ограничивайте список разрешённых методов (GET, POST, PATCH и т. п.), не давайте лишних OPTIONS, PUT, DELETE без причины.
  • Контролируйте заголовки через Access-Control-Allow-Headers и не позволяйте клиентам самоназначать произвольные заголовки, если это не требуется.
  • Добавляйте Vary: Origin, чтобы кэширующие промежуточные звенья корректно обрабатывали разные Origin.
  • Внедрите rate limiting и WAF для защиты от злоупотреблений, даже если CORS настроен правильно.

Примеры настройки на популярных платформах

Практика показывает, что настройки зависят от стека. В простых случаях достаточно middleware, в сложных — гибкой логики и валидации Origin по базе доверенных доменов.

Ниже таблица с ключевыми моментами для Express, nginx и Apache — краткая шпаргалка по заголовкам и рекомендациям.

Платформа Ключевой заголовок Рекомендация
Express (Node.js) Access-Control-Allow-Origin, Access-Control-Allow-Credentials Использовать middleware, проверяющее Origin по списку; не ставить ‘*’ при credentials
nginx add_header Access-Control-Allow-Origin; Динамически подставлять Origin в ответ через переменные и проверку в конфиге; не кэшировать без Vary
Apache Header set Access-Control-Allow-Origin Использовать директивы или модуль, чтобы фильтровать Origin; избегать blanket-правил

Небольшой пример логики на сервере: проверять Origin, сопоставлять с белым списком и только при совпадении добавлять соответствующий заголовок. Такой подход гарантирует, что другие домены не увидят разрешённого ответа.

Короткая иллюстрация: Express

В Express удобнее всего написать middleware, который берёт Origin из запроса и сверяет его со списком доверенных. Если совпадает, в ответ добавляется конкретный Origin и, при необходимости, Allow-Credentials.

Это даёт гибкость: можно хранить белый список в БД или конфигурации окружения и обновлять его без перезапуска сервера. Такой способ часто использовал в проектах с несколькими фронтендами и мобильными приложениями.

Отладка, тестирование и типичные ловушки

При тестировании важно проверять как простые GET-запросы, так и preflight. Инструменты разработчика в браузере покажут, какие заголовки были отправлены и какие возвращены, а также причину блокировки.

Частая ловушка — корректное поведение в локальной среде и неправильное в продакшене из-за прокси, CDN или балансировщика. Убедитесь, что заголовки не перезаписываются и не кэшируются посредниками. Я лично сталкивался с багом, где nginx подставлял универсальный Origin в ответ, что сломало изоляцию между клиентами.

Как CORS вписывается в общую модель безопасности

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

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

Рекомендации для разработки и деплоя

В среде разработки можно временно ослабить ограничения ради скорости работы, но обязательно документировать эти исключения и не переносить их в продакшен. Используйте переменные окружения для управления белыми списками и признаками debug.

На этапе деплоя проверьте конфигурацию через автоматические тесты: имитация запросов с разных Origin, проверка preflight и корректности заголовков Vary и Cache-Control. Это помогает избежать сюрпризов, когда приложение попадает за CDN или прокси.

Небольшой практический совет по мониторингу: логируйте Origin в ошибочных запросах и настраивайте алерты на всплески подозрительной активности. Это часто даёт первые сигналы о попытках автоматизированного сканирования или эксплуатации уязвимости.

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