CORS, CORP, COEP, COOP. Разбираемся с всеми CO* и смотрим на нюансы

0c7a50be63aa1b2b42b6cc242d3ea0bb.jpg

В сети интернет достаточное количество информации на русском языке по поводу SOP и CORS, но введение в такие технологии как CORP, COEP и COOP показалось недостаточным (а кто-то может видит эти аббревиатуры в первый раз). Поэтому решил описать статью по знакомству с CO* политиками.

В данной статье мы немного затронем SOP, подробнее поговорим про CORS, после чего перейдем к CORP, COEP и COOP.

Same Origin Policy

Согласно Same Origin Policy (далее SOP) два JavaScript-контекста могут обращаться к DOM друг друга, если у них совпадают источники (origin) — протокол, домен и порт. Данная политика выполняется браузером и зашита в нем по умолчанию.

источник и сайт
источник и сайт

SOP разрешает встраивать изображения с помощью тега , мультимедиа с помощью тега  и JavaScript с помощью тега 

Значение атрибута crossorigin="anonymous" или атрибут без значения (crossorigin) используется в HTML для указания браузеру, что элемент (например,  ,  ,  ,   и 

При посещении сайта http://evil.com с таким кодом браузер выполнит POST запрос на смену почты /email/change на сайте https://vulnerable-website.com на подконтрольную злоумышленнику. В этом случае, если у жертвы хранятся Cookie для данного сайта и выполняются все условия для атаки, то браузер автоматически подставляет Cookie жертвы в запрос, тем самым выполняя запрос от лица жертвы. В результате можно завладеть профилем жертвы (account takeover).

По этой причине плохой практикой является изменение состояния на сервере через GET запросы. Например, если запрос на смену почты будет выглядеть подобным образом https://vulnerable-website.com/email/change?email=pwned@evil-user.net, то CSRF можно выполнить просто через HTML-тег картинки:

Если использовать для атаки Fetch API, то браузер не будет подставлять Cookie в кросс-доменный запрос, если явно не указать это в коде с использованием credentials: 'include', даже если Fetch запрос будет выполнен в no-cors mode. Однако, пока этот запрос считается простым, способ отправки кросс-доменного запроса не так важен. Как я и писал ранее, браузер поругается на ошибки CORS, но запрос все равно отправит.

// Простой POST-запрос с телом в формате form-data
fetch('https://bank.com/transfer', {
  method: 'POST',
  credentials: 'include', // отправка кук
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: 'amount=1000&to=attacker'
});

Тема CSRF и защита от нее обширна и выходит за рамки данной статьи, поэтому можете ознакомиться с ней самостоятельно.

С отправкой авторизованных запросов понятно, а что на счет чтения ответов на них?

Если мы используем fetch запрос выше из примера с CSRF, браузер заблокирует доступ к ответу, даже если сервер вернет Access-Control-Allow-Origin: * или конкретный домен. Это происходит потому, что браузер ожидает новый для нас CORS заголовок сервера Access-Control-Allow-Credentials: true.

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

8b37b69b941656e294995ec0889b5357.png

Если говорить про кросс-доменные запросы, то, как мы уже знаем, чтобы получить доступ к ресурсу стороннего источника нужно использовать cors mode. Для этого нужно использовать уже знакомый нам атрибут crossorigin. Чтобы получить доступ до авторизованного ответа сервера, нужно выставить в значении атрибута crossorigin="use-credentials".

В данном случае, как и с Fetch API, браузер будет ожидать оба заголовка ACAO и ACAС: true

Итоговая таблица по кросс-доменным простым запросам с использованием различных возможностей браузера:

efe5a0d2ca7d9c634129c66d46368c14.png

Особенности

Отдельно рассмотрим случай, когда для любого запроса сервер одновременно выставляется следующие заголовки:

  • Access-Control-Allow-Origin: *

  • Access-Control-Allow-Credentials: true

Современные браузеры будут блокировать передачу ответов, в которых указана такая политика, в JavaScript-контекст, из которых были сделаны запросы, и поднимать исключение.

Разработчики могут обходить такие блокировки, динамически выставляя значение ACAC из значения заголовка запроса Origin, что является плохой практикой и приводит к уязвимостям.

Существует ещё одна особенность при использовании  в заголовке ACAO. Если в ответе на запрос указан заголовок Set-Cookie, а в заголовке ACAO указана *, то браузер не выставит для пользователя данное значение cookie.

Preflight запрос

Ранее были даны характеристики простых запросов. Всё, что не попадает под описание простого запроса, можно считать «сложным» или «непростым». Например, если JS-код добавит произвольный HTTP-заголовок в запросе (тот же Authorization) или отправит PUT-запрос.

Для этого браузер предварительно отправит так называемый preflight запрос с HTTP-методом OPTIONS, в котором будут перечислены метод и заголовки, добавленные или изменённые в основном запросе (исключением являются заголовки Accept,  Accept-Language, Content-Language,  Content-Type со стандартным для форм значениями, которые не попадут в preflight, если они будут изменены).

d01edc852bbca7eb012f1add812da25b.png

Основной запрос будет отправлен только в том случае, если будет получен ответ на preflight с политикой, явно разрешающей кросс-доменное взаимодействие: в нем будут явно указаны ресурс, с которого ожидается кросс-доменный запрос, заголовки, которые можно изменить или добавить, а также доступные методы. Ответ сервера будет содержать заголовки Access-Control-Allow-*, где вместо * будет содержаться та часть, которой приписывается политика. Например, Access-Control-Allow-Methods описывает разрешенные HTTP методы, а Access-Control-Allow-Headers описывает разрешенные HTTP заголовки.

9c72f8e1a26537d24e761864e0876459.png

Использование preflight запросов так же используется как защита от CSRF, т.к. основной запрос не выполняется автоматически с подстановкой Cookie, когда жертва посещает зловредный сайт. Однако не является хорошей практикой по защите от CSRF (см. доклад по данной теме).

Итоговая табличка по CORS:

7ef3eccef12d3fabc24169127d242764.png

Обычно представленных выше знаний достаточно многим специалистам, поэтому на тему SOP и CORS есть куча статей. Гораздо меньше внимания уделяется другим cross-origin политикам браузера.

CORP

Cross-Origin Resource Policy (CORP) — это настройка безопасности, которая говорит браузеру:»Эти файлы (картинки, скрипты, стили) можно использовать только на моём сайте или источнике. Другие сайты не могут их встраивать! ». Данная политика является настройкой изоляции через браузер и работает только в no-cors mode. Это значит, что ограничения будут касаться только тех ресурсов, которые были загружены и отрендерены без использования CORS, например, с помощью тегов script и img.

Вы так же можете выполнить запрос через fetch, но переведя его в no-cors mode, запрос выполнится, но доступ к ответу вы не получите.

fetch('http://corpsite.ru/someResource', {mode: "no-cors"})

Более формальное определение будет звучать так:»Заголовок ответа HTTP Cross-Origin-Resource-Policy (CORP) указывает, что браузер должен блокировать cross-site или cross-origin запросы к данному ресурсу».

Сервер отправляет специальный заголовок Cross-Origin-Resource-Policy. с следующими значениями:

  1. same-origin — ресурс можно использовать только на том же источнике (например,  example.com→ example.com).

  2. same-site — разрешает использование на поддоменах (вспоминаем, что такое site в контексте браузеров)
    (например,  blog.example.com → example.com).

  3. cross-origin — разрешает встраивать ресурс на любых источниках.

Блокировка браузера при использовании CORP
Блокировка браузера при использовании CORP

Для чего это нужно? Сторонний источник не сможет встроить ваши картинки, видео или скрипт без разрешения (например, если у вас свой CDN). Так же, если у вас есть приватные файлы (например, PDF-документы), их нельзя будет открыть на сторонних сайтах. Помимо этого это надежная защита от атак, подобных Spectre, поскольку позволяет браузерам блокировать данный ответ до того, как он попадет в процесс злоумышленника.

Отличия от CORS:

  • CORS управляет доступом к ресурсам через JavaScript (например, через fetch).

  • CORP управляет встраиванием ресурсов в HTML (теги ,  

    Рейтинг@Mail.ru