Зачем нужен CORS: просто о сложном
Источник

Видели раньше подобное сообщение? Наверняка видели и, скорее всего, довольно часто.

Миллионы статей объясняют, как исправить вышеуказанную ошибку. Но что именно представляет собой этот CORS (cross-origin resource sharing  —  обмен ресурсами с запросом источника данных) и зачем он вообще нужен?

Чтобы ответить на этот вопрос, рассмотрим для начала типичную ситуацию и варианты ее развития.

Допустим, вы зашли на сайт bank.com, который является вашим банковским сервисом. После входа в систему в браузере сохраняется сессионный cookie-файл (который сообщает серверу bank.com о входе используемого вами браузера в ваш аккаунт). Все последующие ваши запросы к bank.com теперь будут содержать этот cookie-файл, и bank.com сможет правильно отреагировать, установив, что именно вы вошли в систему.

Теперь представьте, что вы решили проверить свою электронную почту. Видите подозрительное письмо и, не раздумывая, решаете щелкнуть по указанной в нем ссылке, ведущей на attack.com. Этот сайт отправляет запрос на bank.com, чтобы получить ваши банковские данные. Имейте в виду, что bank.com все еще считает, что это вы вошли в систему благодаря сессионному cookie-файлу, хранящемуся в вашем браузере. Для сервера, связанного с bank.com, все выглядит так, будто вы запросили свои банковские реквизиты обычным способом, и он предоставляет их. Теперь attack.com получает к ним доступ и сохраняет их в другом месте с вредоносным умыслом.

Чтобы исключить подобные ситуации, была принята типовая инструкция для браузеров под названием SOP (Same-Origin Policy  —  стандартные операционные процедуры). Согласно SOP, браузер, заметив, что вы пытаетесь сделать запрос к bank.com откуда-то еще, кроме bank.com, заблокирует этот запрос. Важно понимать, что это процедура, выполняемая браузером. У bank.com нет возможности определить, откуда пришел запрос, поэтому он не может защитить себя от таких атак, как CSRF (cross-site request forgery  —  межсайтовая подделка запросов). Эту роль берет на себя используемый вами браузер. Получая запросы о предоставлении источника данных (схема + доменное имя + порт, https//foo.com:4000, http//bar.org:3000 и т. д.  —  в общем, URL), он будет отправлять только те запросы, которые имеют отношение к тому же источнику. 

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

CSRF

Дело в том, что серверы могут определить, откуда пришел запрос. Заголовок “Origin” (источник данных), который должен быть у запросов, показывает, от какого источника поступил запрос. Например, в приведенном выше примере запрос будет выглядеть следующим образом:

Request to -----> bank.com
{
Headers: { Origin: http://attack.com }
}

Теоретически bank.com должен проверять подобную информацию, чтобы реагировать только на запросы с соответствующим “Origin”. Обычно так и происходит, поэтому SOP оказывается несколько ограничивающим фактором.

Вот тут-то и приходит на выручку CORS.

CORS

Когда веб-приложение с сайта example.com пытается запросить ресурсы с сайта bank.com, браузер автоматически включает в запрос заголовок “Origin”, указывающий, откуда исходит запрос (example.com). Вот тут-то и кроется важная деталь: вместо того чтобы сразу блокировать такие межплатформенные запросы источника данных в соответствии с SOP, сервер bank.com может проверить заголовок “Origin” и на основе собственной политики CORS принять решение  —  разрешить или запретить запрос.

Если bank.com посчитает example.com заслуживающим доверия или отнесет его к разряду общедоступных ресурсов, то может ответить специальными CORS-заголовками, такими как Access-Control-Allow-Origin, указывающими, каким источникам разрешен доступ к ресурсу. Этот заголовок может иметь значение http://example.com, явно разрешающее данный источник, или * для общедоступных ресурсов, к которым может получить доступ любой источник.

Конечно, браузер облегчает все это. Если что-то пойдет не так, вы получите ошибку.

А что, если в запросе нет заголовка “Origin”? Что, если он содержит кучу других заголовков и не использует ни один из основных методов HTTP?

В таких ситуациях работа с CORS становится немного сложнее, поскольку речь идет уже не о “простом запросе”. Именно здесь вступает в силу CORS-концепция предварительного запроса (preflight).

Предварительный запрос

Некоторые типы запросов, использующие такие HTTP-методы, как PUT и DELETE, или заголовки, которые не включаются автоматически в каждый запрос, могут изменить данные на сервере. В таких случаях браузеры отправляют предварительный запрос (preflight), прежде чем выполнить фактический запрос. Этот предварительный запрос представляет собой запрос HTTP OPTIONS, цель которого  —  проверить, безопасно ли отправлять на сервер фактический запрос.

Предварительный запрос включает заголовки, описывающие HTTP-метод и заголовки фактического запроса. Затем происходит следующее:

  1. Ответ сервера. Если сервер поддерживает инструкцию CORS и фактический запрос, то отвечает на предварительный запрос заголовками, указывая, какие методы и заголовки разрешены. Это могут быть такие заголовки, как Access-Control-Allow-Methods и Access-Control-Allow-Headers.
  2. Решение браузера. Основываясь на ответе сервера на предварительный запрос, браузер принимает решение о том, стоит ли продолжать выполнение запроса. Если ответ сервера указывает на то, что запрос разрешен, браузер отправляет его; если нет  —  браузер блокирует запрос, и вы увидите ошибку, связанную с CORS.

Заключение

Надеюсь, концепция CORS стала вам понятней. Самое главное, на мой взгляд, чтобы код вашего сервера соответствовал инструкции браузера. Это необходимо для вашей безопасности. Если вы пользуетесь Chrome, можете не слишком беспокоиться, если нажмете на неправильные ссылки (хотя бдительность и здесь не помешает). Однако не стоит всецело полагаться на SOP. Пользуясь каким-нибудь сторонним браузером, не соответствующим стандартам, вы лишаете себя гарантий безопасности. Вот почему нужно внимательно следить за тем, какое программное обеспечение вы используете!

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи Oleks Gorpynich: CORS Finally Explained — Simply. How does CORS actualy work?

Предыдущая статьяКорутины в Kotlin: топ-50 вопросов для собеседования с Android-разработчиками в 2024 году 
Следующая статьяМенеджеры пакетов NPM, PNPM и YARN