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

Проблема

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

Некоторые запросы, конечно, не инициируются (напрямую) пользователем. Большая часть мощности современных веб-приложений исходит из способности JavaScript независимо взаимодействовать с сервером; запрос данных, обновление данных и отображение их пользователю.

Как правило, это нормально. Но все становится сложнее, когда эти запросы отправляются сценариями в разных доменах.

Представьте, что вы вошли в свой банковский счет. После того, как вы вошли в систему, банковское приложение создает файл cookie. Когда вы переходите к проверке своих счетов (переходите на разные страницы, нажимаете кнопки и т. д.), браузер отправляет запросы на сервер банковского приложения.

Теперь предположим, что, увидев дополнительные средства на своем счету, вы решили заказать себе небольшой отпуск. К сожалению, ваши поиски доступного жилья привели вас на веб-сайт Shady Motel, на котором размещен вредоносный скрипт. Когда вы загружаете домашнюю страницу Shady Motel, вредоносный скрипт запускается и пытается отправить запрос в ваше банковское приложение, чтобы украсть вашу конфиденциальную информацию.

Как браузер защищает вашу конфиденциальную банковскую информацию от компрометации в этой ситуации?

Великая стена разделения

Чтобы гарантировать, что вредоносные скрипты не будут отправлять несанкционированные запросы на другие веб-сайты от имени пользователя, браузеры применяют набор правил, называемый политикой того же источника. Браузер, который реализует политику одного и того же источника (которая включает все самые популярные современные браузеры), запрещает скриптам доступ к данным из другого источника.

Так что же такое «происхождение»?

Вообще говоря, источник — это комбинация схемы URI, имени хоста и номера порта. Это «достаточно хорошее» приближение для большинства ситуаций, но можно столкнуться с пограничными случаями, когда браузеры будут вести себя иначе или неоднозначно.

RFC 6454, раздел 4, содержит рекомендации о том, как точно определить, являются ли два источника одинаковыми, но реальное определение того, что представляет собой разные источники, в конечном итоге зависит от реализации браузера.

В случае сомнений обратитесь к документации соответствующего браузера(ов).

Путь сквозь стену

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

Как разработчик, вы захотите, чтобы ваши скрипты могли вызывать сторонние API. А как поставщик стороннего API, вы хотите, чтобы законные клиентские приложения могли получить доступ к вашему веб-сервису.

Здесь на помощь приходит CORS.

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

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

CORS различает два типа запросов, обычно называемых «простыми» и «предварительно проверенными».

Запросы GET, запросы HEAD и некоторые запросы POST (с типами MIME text/plain, application/x-www-form-urlencoded или multipart/form-data) называются «простыми запросами».

Другие HTTP-запросы проходят «предварительную проверку», чтобы избежать непреднамеренных обновлений на сервере. Это означает, что браузер отправит HTTP-запрос OPTIONS на сервер, который вернет подробную информацию, чтобы узнать, позволит ли сервер вызывающему сценарию выполнить требуемый HTTP-запрос. Если заголовки, возвращенные из запроса OPTIONS, указывают на то, что предполагаемый вызов клиента разрешен, выполняется последующий HTTP-запрос.

Заголовки CORS HTTP

CORS использует некоторую часть стандартного набора заголовков HTTP, в зависимости от того, является ли запрос простым или предварительным.

Заголовки запроса

Клиентские приложения могут использовать CORS, отправляя ряд необходимых заголовков запроса. Если вы делаете запрос AJAX, вам не нужно устанавливать эти заголовки программно (браузер добавит их за вас).

Origin: <uri>

Заголовок Origin просто указывает, с какого сервера инициирован запрос (только имя сервера, а не путь).

Access-Control-Request-Headers: <method>

В предварительных запросах используется заголовок Access-Control-Request-Method, чтобы сообщить серверу, какой метод будет использоваться в последующем (фактическом) HTTP-запросе.

Access-Control-Request-Headers: <field-name(s)>

В предварительных запросах используется заголовок Access-Control-Request-Method, чтобы сообщить серверу, какие заголовки будут использоваться в последующем (фактическом) HTTP-запросе.

Заголовки ответа

Сервер использует заголовки ответа CORS, чтобы сообщить клиентам, как они могут безопасно получить доступ к ресурсам сервера.

Access-Control-Allow-Origin: <origin> | *

Заголовок Access-Control-Allow-Origin может использоваться сервером, чтобы указать, что сценарии из определенного источника могут получить доступ к ресурсу или, что любой источник может получить доступ к ресурсу (используя подстановочный знак «*» .)

Access-Control-Expose-Headers: <header-name>[, <header-name>]*

Заголовок Access-Control-Expose-Headers может использоваться сервером для указания того, к каким заголовкам ответа браузер должен иметь доступ.

Access-Control-Max-Age: <seconds>

Браузеры могут повысить производительность CORS, кэшируя результаты предварительных запросов. Однако это создает проблему безопасности, поскольку кэшированная политика может не отражать последние изменения политики на сервере. Заголовок Access-Control-Max-Age сообщает браузеру, сколько секунд ответ предварительной проверки может быть безопасно кэширован, прежде чем браузер должен проверить наличие возможных обновлений на сервере.

Access-Control-Allow-Methods: <method>[, <method>]*

По умолчанию браузер не будет отправлять файлы cookie или другие учетные данные вместе с запросом. Заголовок Access-Control-Allow-Credentials разрешает отправку учетных данных.

(ПРИМЕЧАНИЕ. Если для флага Access-Control-Allow-Credentials установлено значение true, вы не можете использовать подстановочное значение с заголовком Access-Control-Allow-Origin.)

Access-Control-Allow-Methods: <method>[, <method>]*

Что нужно учитывать разработчикам интеграции

  • Планируйте заранее. Убедитесь, что сторонние веб-службы, к которым вам нужно получить доступ из сценариев на стороне клиента, имеют политику белого списка CORS.
  • Поймите, кто ваши клиентские приложения. Если клиентскому коду требуется доступ к вашим API, убедитесь, что ваше серверное приложение поддерживает CORS и может отличать известных и неизвестных клиентов.
  • Проверьте такие ресурсы, как MDN и OWASP Foundation, чтобы убедиться, что ваше приложение безопасно и соответствует стандартам.