---
layout: post
title: Совместное использование ресурсов между разными источниками (CORS)
subhead: Безопасный доступ к ресурсам на других доменах
authors:
  - kosamari
date: 2018-11-05
description: Политика одинакового источника блокирует в браузере чтение ресурса из другого источника. Этот механизм не позволяет вредоносному сайту читать данные другого сайта, но он также препятствует правомерному применению. Что если вы захотите получить данные о погоде в другой стране? Включение CORS позволяет серверу сообщить браузеру, что ему разрешено использовать дополнительный источник.
tags:
  - security
---

Политика одинакового источника блокирует в браузере чтение ресурса из другого источника. Этот механизм не позволяет вредоносному сайту читать данные другого сайта, но он также препятствует правомерному применению. Что если вы захотите получить данные о погоде в другой стране?

В современном веб-приложении приложение часто хочет получать ресурсы из другого источника. Например, вы хотите получить данные JSON из другого домена или загрузить изображения с другого сайта в элемент `<canvas>`.

Другими словами, существуют **общедоступные ресурсы**, которые должны быть доступны для чтения всем, но политика одинакового источника блокирует эту возможность. Разработчики использовали обходные пути, такие как [JSONP](https://stackoverflow.com/questions/2067472/what-is-jsonp-all-about), но благодаря механизму **совместного использования ресурсов между разными источниками (CORS)** браузер может предоставлять страницам доступ к ресурсам другого домена.

Включение **CORS** позволяет серверу сообщать браузеру, что ему разрешено использовать дополнительный источник.

## Как работает запрос ресурса в Интернете?

<figure data-float="right">{% Img src="image/tcFciHGuF3MxnTr1y5ue01OGLBn2/8J6A0Bk5YXdvyoj8HVzs.png", alt="запрос и ответ", width="668", height="327" %} <figcaption> Рисунок: иллюстрированный запрос клиента и ответ сервера</figcaption></figure>

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

HTTP-заголовок используется для согласования типа обмена сообщениями между клиентом и сервером и применяется для определения доступа. И запрос браузера, и ответное сообщение сервера разделены на две части: **header (заголовок)** и **body (тело)**:

### header

Информация о сообщении, такая как тип сообщения или кодировка сообщения. Заголовок может включать в себя [различные сведения](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields), выраженные парами «ключ-значение». Заголовок запроса и заголовок ответа содержат разную информацию.

{% Aside %} Важно отметить, что заголовки не могут содержать комментарии. {% endAside %}

**Пример заголовка запроса**

```text
Accept: text/html
Cookie: Version=1
```

Вышеупомянутое эквивалентно высказыванию: «Я хочу получить в ответ HTML. Вот файл cookie, который у меня есть».

**Пример заголовка ответа**

```text
Content-Encoding: gzip
Cache-Control: no-store
```

Вышеупомянутое эквивалентно высказыванию: «Данные закодированы с помощью gzip. Не кешируйте это, пожалуйста».

### body

Само сообщение. Это может быть обычный текст, двоичное изображение, JSON, HTML и т. д.

## Как работает CORS?

Помните, что политика одинакового источника предписывает браузеру блокировать запросы между разными источниками. Если вы хотите получить общедоступный ресурс из другого источника, сервер, предоставляющий ресурсы, должен сообщить браузеру: «Данный источник запроса может получить доступ к моему ресурсу». Браузер запоминает это и разрешает совместное использование ресурсов между разными источниками.

### Шаг 1: запрос клиента (браузера)

Когда браузер делает запрос на другой источник, он добавляет заголовок `Origin` с текущим источником (схема, хост и порт).

### Шаг 2: ответ сервера

На стороне сервера, когда сервер видит этот заголовок и хочет разрешить доступ, он должен добавить к ответу заголовок `Access-Control-Allow-Origin` с указанием источника запроса (или `*` для разрешения любого источника).

### Шаг 3: браузер получает ответ

Когда браузер видит этот ответ с соответствующим заголовком `Access-Control-Allow-Origin`, браузер разрешает передачу данных ответа сайту клиента.

## CORS в действии

Вот крошечный веб-сервер, использующий Express.

{% Glitch { id: 'cors-demo', path: 'server.js', height: 480 } %}

Первая конечная точка (строка 8) не имеет установленного заголовка ответа, она просто отправляет файл в ответ.

{% Instruction 'devtools' %} {% Instruction 'devtools-console', 'ul' %}

- Попробуйте следующую команду:

```js
fetch('https://cors-demo.glitch.me/', {mode:'cors'})
```

Вы должны увидеть сообщение об ошибке:

```bash
request has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header
is present on the requested resource.
```

Вторая конечная точка (строка 13) отправляет тот же файл в ответ, но добавляет в заголовок `Access-Control-Allow-Origin: *`. С консоли наберите:

```js
fetch('https://cors-demo.glitch.me/allow-cors', {mode:'cors'})
```

На этот раз ваш запрос не должен блокироваться.

## Учетные данные и CORS

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

### Запрос

Добавьте `credentials: 'include'` в параметры выборки, как показано ниже. Это включит файл cookie в запрос.

```js
fetch('https://example.com', {
  mode: 'cors',
  credentials: 'include'
})
```

### Ответ

Для заголовка `Access-Control-Allow-Origin` необходимо указать конкретный источник (без подстановочного знака `*`), а для параметра `Access-Control-Allow-Credentials` следует установить значение `true`.

```text
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Credentials: true
```

## Предварительные запросы для сложных HTTP-вызовов

Если веб-приложению требуется сложный HTTP-запрос, браузер добавляет **[предварительный запрос](https://developer.mozilla.org/docs/Web/HTTP/CORS#preflighted_requests)** в начало цепочки запросов.

Спецификация CORS определяет **сложный запрос** как

- запрос, использующий методы, отличные от GET, POST или HEAD;
- запрос, который включает заголовки, отличные от `Accept`, `Accept-Language` или `Content-Language`;
- запрос с `Content-Type` отличным от `application/x-www-form-urlencoded`, `multipart/form-data` или `text/plain`.

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

```text
OPTIONS /data HTTP/1.1
Origin: https://example.com
Access-Control-Request-Method: DELETE
```

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

```text
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, DELETE, HEAD, OPTIONS
```

Ответ сервера может также включать заголовок `Access-Control-Max-Age` для указания продолжительности кеширования результатов предварительного запроса (в секундах), чтобы клиенту не нужно было делать предварительный запрос каждый раз при отправке сложного запроса.
