Сервис разрешает браузерным запросам с любого источника передавать учётные данные. Какой основной риск создаёт такая настройка?
Такая настройка может позволить вредоносному сайту отправлять запросы от имени пользователя и читать ответы защищённого сервиса. Это происходит, если сервер разрешает произвольный источник вместе с передачей учётных данных, например cookies.
Основной риск — неправильная конфигурация CORS. Она нарушает ожидаемую границу доверия между веб-источниками и может раскрыть персональные данные или результаты административных операций.
Браузеры используют политику единого источника (Same-Origin Policy), чтобы скрипт одного сайта не мог произвольно читать ответы другого сайта. По мере развития веб-приложений потребовался контролируемый способ разрешать безопасное взаимодействие между разными источниками.
CORS стал механизмом такого контролируемого исключения: сервер явно сообщает браузеру, каким источникам разрешено читать ответы и допускается ли передача учётных данных. Проблема возникает, когда это разрешение выдают без ограничения доверенных источников.
Предположим, пользователь вошёл в корпоративный сервис, а затем открыл вредоносную страницу. Если API разрешает запросы с любого источника и принимает cookies пользователя, скрипт вредоносной страницы может инициировать запрос к API.
При корректно настроенной защите браузер может отправить запрос, но не позволит вредоносному скрипту прочитать ответ. При ошибочной конфигурации скрипт получает данные, доступные пользователю: профиль, документы, токены операций или результаты административных запросов.
Риск зависит от нескольких условий: браузерного сценария, наличия действующих учётных данных, политики SameSite для cookies и того, какие операции разрешены пользователю. Кроме того, CORS в первую очередь управляет чтением ответов браузерным JavaScript, а не является универсальным механизмом авторизации.
При тестировании нужно проверить, как сервер обрабатывает заголовок Origin. Нельзя считать безопасным ответ только потому, что он содержит CORS-заголовки: важно установить, разрешается ли конкретный доверенный источник, произвольный источник или значение отражается обратно без проверки.
Для защищённого API обычно задают явный список доверенных источников. Сравнение должно быть точным: учитываются схема, хост и порт; частичное совпадение строк или доверие поддоменам без отдельного обоснования опасны. Передача учётных данных должна разрешаться только там, где это действительно необходимо.
Пример безопаснее отражения любого источника:
Здесь сервер разрешает чтение ответа только указанному источнику. Vary: Origin важен при кэшировании ответов: без него промежуточный кэш может отдать ответ, сформированный для одного источника, другому.
Разрешение одного источника не заменяет проверку авторизации на сервере. CORS также не должен использоваться как защита от CSRF: запрос, изменяющий состояние, может быть отправлен в некоторых сценариях даже тогда, когда браузер запрещает прочитать его результат. Для таких операций нужны отдельные меры, например проверка CSRF-токена, подходящая политика cookies и серверная авторизация.
Более строгий вариант — вообще не разрешать credentialed CORS для административных или чувствительных API, если интеграция в нём не нуждается. Компромисс состоит в том, что строгий allowlist требует сопровождения при добавлении легитимных клиентов, но заметно уменьшает поверхность атаки по сравнению с разрешением любого источника.
У API интернет-магазина был веб-клиент и отдельная партнёрская панель. Для ускорения интеграции команда стала возвращать в Access-Control-Allow-Origin значение из входного Origin, а передачу cookies включила для всех запросов. В результате любой сайт мог получить ответы API в контексте вошедшего пользователя.
Рассматривались три варианта. Полное отключение CORS быстро устраняло чтение ответов из браузера, но ломало легитимную партнёрскую панель. Разрешение любого источника сохраняло совместимость, но оставляло уязвимость. Создание точного allowlist для клиентского сайта и панели потребовало изменить конфигурацию и тесты, зато ограничило доверие необходимыми источниками.
Выбрали третий вариант, отдельно проверили credentialed-запросы, preflight-запросы, кэширование и операции изменения состояния. После этого произвольный сайт перестал читать ответы API, а разрешённые клиенты сохранили работу; серверные проверки авторизации и CSRF-защита остались обязательными.
Для публичных данных это иногда приемлемо, но решение зависит от содержания ответов и других способов аутентификации. Если ответ содержит чувствительную информацию, её нельзя считать публичной только из-за отсутствия cookies: применяются токены в заголовках, браузерные учётные данные и логика конкретного клиента.
Кроме того, такое разрешение не защищает от CSRF для механизмов аутентификации, которые браузер отправляет автоматически. Поэтому отсутствие credentialed CORS снижает риск чтения ответа вредоносным скриптом, но не отменяет необходимость серверной проверки происхождения опасных запросов и защиты от CSRF.
Если сервер без проверки копирует любой полученный Origin в Access-Control-Allow-Origin и одновременно разрешает учётные данные, он фактически превращает любой сайт в доверенный. Браузер видит согласованный источник и разрешает скрипту прочитать ответ.
Безопасным является не само отражение, а отражение только после точного сопоставления с управляемым allowlist. Нужно также учитывать варианты источника вроде другого порта, HTTP вместо HTTPS, неожиданных поддоменов и значение null, если бизнес-логика не требует их поддержки.
Нет. CORS — это браузерный механизм, ограничивающий доступ скриптов к ответам. Клиент вне браузера может отправить HTTP-запрос независимо от CORS-заголовков, поэтому API обязан самостоятельно проверять аутентификацию, авторизацию, входные данные и допустимость операции.
Следовательно, тест должен включать как браузерный сценарий проверки чтения ответа из недоверенного источника, так и прямой запрос к API. Если прямой запрос позволяет получить или изменить данные без надлежащих серверных проверок, проблема уже не сводится к CORS и имеет более фундаментальный характер.