API, который заработал после добавления Access-Control-Allow-Origin, не обязательно безопасен. CORS нельзя добавить один раз и забыть; важно сузить, кому вы разрешаете. Разберём, как он работает и как настроить его безопасно (без шагов атаки).
Сначала о том, как это работает
Браузеры применяют политику одного источника: JS другого источника не может свободно читать ответы другого сайта. CORS существует, чтобы открыть это исключение только тем, кому сервер явно разрешает.
Источник (origin) — это сочетание схемы (https), имени хоста и порта. У https://example.com и https://api.example.com разные хосты, значит, это разные источники; у http://example.com и https://example.com разные схемы, значит, они тоже разные.
| Заголовок ответа | Значение |
|---|---|
Access-Control-Allow-Origin | Каким источникам можно читать ответ |
Access-Control-Allow-Credentials | Разрешены ли запросы с учётными данными (куками) |
Access-Control-Allow-Methods / -Headers | Какие HTTP-методы/заголовки разрешены |
Суть: CORS находится на стороне «ослабления ограничения». Ослабьте его неверно — и данные, которые не должны быть читаемы, становятся читаемыми для третьих сторон.
Важно и ещё одно: CORS останавливает именно чтение ответа JavaScript. Простой запрос доходит до сервера и обрабатывается даже без разрешения CORS. В случаях вроде пользовательских заголовков браузер сначала проверяет запросом OPTIONS (preflight) и не отправляет настоящий запрос, если это не разрешено.
Чем он отличается от похожих механизмов
CORS часто путают с механизмами с похожими названиями и с близкой уязвимостью.
| Термин | Что определяет | Кто обеспечивает |
|---|---|---|
| Политика одного источника | Другие источники не могут читать ответы (поведение браузера по умолчанию) | Браузер |
| CORS | Каким источникам, в виде исключения, можно читать | Сервер объявляет, браузер обеспечивает |
| CSRF | Атака, заставляющая браузер вошедшего пользователя отправлять непреднамеренные запросы | Предотвращается CSRF-токенами и SameSite |
| CSP | Какие скрипты и соединения может загружать ваша собственная страница | Сервер объявляет, браузер обеспечивает |
CORS — о том, «можно ли прочитать ответ»; CSRF — о том, «можно ли отправить запрос от имени пользователя». Ужесточение CORS не предотвращает CSRF.
Что делает настройку опасной
Опасные настройки (частые)
- Отражение Origin запроса обратно как разрешённого
Access-Control-Allow-Origin: *плюс разрешение учётных данных- Открытие API широко кросс-доменно, когда это не нужно
Безопасные настройки
- Allowlist (только фиксированные доверенные источники)
- Запрет по умолчанию для всего остального
- Для запросов с учётными данными — конкретный источник, а не
*
Как обычно возникают неправильные настройки
Большинство неправильных настроек появляется из попытки избавиться от ошибки CORS. Типичная последовательность:
- Вызов API с фронтенда с куками даёт ошибку CORS
- Вы добавляете
Access-Control-Allow-Origin: *. Но для запросов с учётными данными браузер не даёт странице прочитать ответ с*, и ошибка остаётся - Вы переходите на отражение пришедшего
Origin, и ошибка исчезает. Одновременно любой сайт теперь может читать ответы с активным сеансом
Даже с allowlist небрежное сравнение ведёт туда же.
- Сравнение «оканчивается на
example.com» пропускает и другой домен, напримерevil-example.com - Сравнение «начинается с
https://example.com» пропускает иhttps://example.com.evil.net - Разрешение источника
nullдля удобства разработки разрешает и чтение из источниковnull, которые могут создать другие сайты
Сравнивайте источник целиком (схему, хост и порт) на точное совпадение.
Основы безопасной настройки
Используйте allowlist (самое важное)
Не отражайте Origin вслепую
Origin запроса прямиком в Access-Control-Allow-Origin. Возвращайте его только когда он совпадает с allowlist. Поскольку значение теперь зависит от запроса, отправляйте также Vary: Origin, чтобы кэши не путали ответы.Никогда не используйте * с учётными данными
Access-Control-Allow-Origin: * с Allow-Credentials: true. Вместо этого указывайте конкретный источник.Минимизируйте то, что открываете
* только для публичных данных, для которых не нужен вход.Как проверить свой сайт
Отправьте своему API запрос от имени другого источника и посмотрите на заголовки ответа. Делайте это только с API, которыми вы управляете.
# Указываем источник, который вы не разрешали, и смотрим только заголовки ответа
curl -s -o /dev/null -D - -H "Origin: https://example.com" https://your-api.example/ | grep -i "^access-control"- Если приходит
Access-Control-Allow-Origin: https://example.com, API отражает Origin. Добавьте сверку с allowlist - Если одновременно есть
Access-Control-Allow-Origin: *иAccess-Control-Allow-Credentials: true, пересмотрите настройку - Если ничего не приходит или приходит только источник вашего фронтенда, всё в порядке
Повторите ту же проверку с Origin: null. Для сайта, владение которым вы подтвердили, аудит безопасности сайта проверяет отражение источника и настройки учётных данных вместе. Остальные заголовки безопасности можно проверить проверщиком заголовков безопасности.
Мнение этого сайта: CORS — это «как ослабляешь», а не «защита» — сужайте, кому открываете
Частое заблуждение — «добавление заголовков CORS делает безопасно». Всё наоборот: CORS ослабляет ограничение браузера. Поэтому важно не то, «как добавить», а то, чтобы «минимизировать, кому и насколько широко открываешь». На этом сайте мы отделяем данные, которые можно читать извне, от тех, которые нельзя, и рекомендуем запрет по умолчанию с allowlist, открывающим только исключения. Заметьте, что XSS (который ломает предпосылку о чтении) и CSRF (который нацелен на изменения состояния) — это отдельные уязвимости: один лишь CORS не покрывает всё.
Источники (первичные)
- MDN Web Docs, «Cross-Origin Resource Sharing (CORS)» — developer.mozilla.org (запрет * для запросов с учётными данными, условия preflight, Vary: Origin)
- WHATWG Fetch Standard, «CORS protocol» — fetch.spec.whatwg.org
- OWASP HTML5 Security Cheat Sheet — cheatsheetseries.owasp.org (разрешать только доверенные домены; не возвращать заголовок Origin без проверки)
- PortSwigger Web Security Academy, «Cross-origin resource sharing (CORS)» — portswigger.net (ошибки сверки с allowlist, источник null, CORS — не защита от CSRF)
- MITRE CWE-942: Permissive Cross-domain Policy with Untrusted Domains — cwe.mitre.org
Читать дальше
- Глоссарий: Что такое XSS · Что такое CSRF · Что такое JWT
- Обучение: Как выбирать заголовки безопасности (на английском)
- Проверка: Проверщик заголовков безопасности · Аудит безопасности сайта