CORS и fetch

CORS (Cross-Origin Resource Sharing) — это механизм, который позволяет веб-браузерам ограничивать доступ к ресурсам, расположенным на других доменах. Этот механизм играет важную роль в обеспечении безопасности при взаимодействии с удалёнными API и другими сервисами, работающими в сети.

Проблемы, связанные с кросс-доменными запросами

Когда веб-страница на одном домене (например, example.com) пытается выполнить запрос к серверу на другом домене (например, api.example.org), браузер по умолчанию блокирует такой запрос из-за политики однообразного происхождения (Same-Origin Policy). Эта политика предназначена для защиты пользователя от потенциально опасных веб-страниц, которые могут попытаться получить доступ к данным другого сайта без разрешения.

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

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

Когда браузер пытается выполнить кросс-доменный запрос, он сначала отправляет предварительный запрос типа OPTIONS на целевой сервер. Этот запрос называется “preflight”. В ответ на него сервер должен отправить заголовки, которые указывают, разрешает ли он такие запросы с конкретного домена. Например, сервер может отправить заголовок:

Access-Control-Allow-Origin: https://example.com

Этот заголовок позволяет домену example.com отправлять запросы на сервер. Если сервер не отправит соответствующие заголовки, браузер заблокирует запрос.

fetch и CORS

fetch — это стандартный API в JavaScript для выполнения HTTP-запросов. Он поддерживает CORS из коробки, и его поведение зависит от того, как настроены заголовки на сервере.

Основы использования fetch с CORS

Когда вызывается метод fetch для выполнения кросс-доменного запроса, браузер автоматически проверяет заголовки ответа на соответствие политике CORS. Если запрос был выполнен корректно и сервер разрешает доступ, данные будут доступны для обработки в JavaScript.

Пример простого кросс-доменного запроса с использованием fetch:

fetch('https://api.example.org/data', {
    method: 'GET',
    headers: {
        'Content-Type': 'application/json'
    },
    mode: 'cors'  // Указывает, что запрос должен использовать CORS
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));

В этом примере:

  • mode: ‘cors’ — указывает, что запрос должен использовать CORS.
  • headers: { ‘Content-Type’: ‘application/json’ } — указывает тип отправляемых данных.
Опции CORS в fetch

Основная опция для настройки CORS в fetch — это mode. Этот параметр может принимать следующие значения:

  • cors — стандартный режим CORS. Браузер выполняет все необходимые предварительные запросы и заголовки, если сервер это поддерживает.
  • no-cors — используется, если вы хотите отправить запрос, но не ожидаете получить доступ к данным ответа. Это ограничивает доступ к содержимому ответа, например, нельзя читать тело ответа.
  • same-origin — запросы выполняются только к ресурсам того же домена, с которого был загружен сайт.
  • navigate — применяется для загрузки ресурсов, которые могут быть частью навигации в браузере (например, загрузка страницы).

Пример использования no-cors:

fetch('https://api.example.org/data', {
    method: 'GET',
    mode: 'no-cors'
})
.then(response => {
    console.log('Request was successful, but cannot access response data due to CORS policy.');
})
.catch(error => console.error('Error:', error));

В этом случае ответ от сервера будет недоступен в JavaScript, даже если запрос был успешным. Это часто используется для выполнения одноразовых запросов, например, при отправке данных на сервер для анализа.

Ошибки CORS и их обработка

В случае, если сервер не поддерживает CORS, или если заголовки ответа не соответствуют ожиданиям браузера, запрос будет заблокирован, и это приведёт к ошибке, которую можно обработать через .catch().

Пример обработки ошибки CORS:

fetch('https://api.example.org/data', {
    method: 'GET',
    mode: 'cors'
})
.then(response => {
    if (!response.ok) {
        throw new Error('Network response was not ok');
    }
    return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('CORS error or other:', error));

Если сервер не разрешает кросс-доменные запросы, ошибка будет выброшена, и будет выведено сообщение о том, что запрос не удался.

Решение проблем CORS на сервере

Если сервер не поддерживает CORS, то единственный способ разрешить доступ — это настройка правильных заголовков на серверной стороне. Основной заголовок для разрешения CORS — Access-Control-Allow-Origin.

Пример серверной настройки для разрешения CORS на домене example.com:

Access-Control-Allow-Origin: https://example.com

Для разрешения всех доменов можно использовать:

Access-Control-Allow-Origin: *

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

Сервер может также настроить дополнительные заголовки, такие как Access-Control-Allow-Methods для указания разрешённых HTTP-методов (например, GET, POST, PUT), а также Access-Control-Allow-Headers для разрешения определённых заголовков.

Пример разрешения методов и заголовков:

Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type

Важность правильной настройки CORS

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

Кроме того, для некоторых типов запросов, например, с методами PUT или DELETE, или при использовании нестандартных заголовков, браузер будет посылать предварительный запрос OPTIONS. Сервер должен корректно обрабатывать такие запросы, чтобы избежать блокировки.

Таким образом, взаимодействие с кросс-доменными ресурсами требует как настройки серверных заголовков, так и грамотной работы с API на стороне клиента.