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

CORS И ПРЕДВАРИТЕЛЬНЫЕ ЗАПРОСЫ

CORS (Cross-Origin Resource Sharing) — это стандарт безопасности, который позволяет клиентам (браузерам) использовать ресурсы (URL) с других доменов. Он был создан для улучшения политики одинакового источника (SOP — Same-Origin Policy), которая применяется браузерами для ограничения доступа к ресурсам на других доменах, которые они не контролируют.

Примеры ошибок CORS

Ошибки CORS могут появляться в консоли браузера. Вот несколько примеров:

  • Отсутствует заголовок 'Access-Control-Allow-Origin' на запрашиваемом ресурсе.
  • Запрос с другого источника заблокирован: политика одинакового источника не позволяет читать удаленный ресурс.
  • Доступ к fetch на 'https://example.com' с источника 'http://localhost:3000' заблокирован политикой CORS.

Содержание

  • 💡 Пример
  • ⚠️ Запрос с другого источника
  • 🔒 Что такое SOP (политика одинакового источника)?
  • 🔓 Что такое CORS (обмен ресурсами между разными источниками)?
  • ‼️ Предварительные запросы
  • Важные заголовки предварительного запроса и ответа сервера
  • Когда выполняется предварительный запрос?
  • Кэширование ответов на предварительные запросы
  • ❗️ Простые запросы
  • 🟢 Заголовки ответа CORS
  • 🛡 Преимущества работы с CORS
  • 👍 Лучшие практики для реализации CORS

💡 ПРИМЕР

Представьте, что вы создаете веб-сервис, где пользователи могут сохранять свои посты онлайн. Ваш сайт (например, myapp.com) отправляет данные на внешний сервер (например, noteapi.com) с помощью метода POST.

Пример JavaScript-кода, который отправляет пост:


const apiUrl = "https://noteapi.com/api/notes";
const userToken = "authorization_token";
const payload = {
title: "Пост дня",
content: "Сегодня я узнал, как отправлять данные на другой сервер"
};

fetch(apiUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": Bearer ${userToken}
},
body: JSON.stringify(payload)
});

Как браузер выполняет запрос?

  1. Предварительный запрос: Поскольку Content-Type = application/json, перед отправкой основного POST запроса, браузер автоматически выполняет предварительный запрос с помощью метода OPTIONS к серверу noteapi.com. Это нужно, чтобы узнать, разрешен ли такой запрос.

  2. Заголовки в предварительном запросе: Браузер отправляет такие заголовки:

    • Access-Control-Request-Method: POST
    • Access-Control-Request-Headers: Content-Type, Authorization
    • Origin: https://myapp.com
  3. Ответ сервера на предварительный запрос: Если сервер настроен правильно, он отвечает, что запрос с указанными заголовками и методом разрешен с источника https://myapp.com.

  4. Основной запрос: Только после положительного ответа на предварительный запрос браузер отправляет основной POST запрос.

⚠️ ЗАПРОС С ДРУГОГО ИСТОЧНИКА

Простыми словами, это запрос на другой домен. Например, когда мы вставляем изображение на своем сайте с помощью тега <img> с ссылкой на изображение с другого сайта, браузер выполняет запрос с другого источника. Обычно с тегом <img> проблем нет, потому что для него политика специфическая.

Чтобы запрос считался кросс-доменным, могут различаться не только домены, но и "схема" (например, https) или "порт" (например, 80).

Примеры тэгов, которые могут выполнять запросы с другого источника:

  • <img>
  • <script> (если внутри скрипта выполняется запрос, он должен следовать CORS)
  • <frame>
  • <video>
  • <audio>
  • <iframe> (может быть ограничен SOP)
  • <link>
  • <form>

🔒 ЧТО ТАКОЕ SOP (ПОЛИТИКА ОДИНАКОВОГО ИСТОЧНИКА)?

SOP (Same-Origin Policy) — это функция безопасности в браузере, которая ограничивает доступ к ресурсам веб-приложений. Она требует, чтобы ресурс находился на том же источнике, что и веб-приложение, запрашивающее ресурс.

SOP была введена в Netscape Navigator 2.02 в 1995 году и поддерживается всеми современными браузерами.

Чтобы два источника считались одинаковыми, они должны иметь одинаковый хост, схему и порт. Например, рассмотрим URL https://blog.postman.com:3000:

  • хост — blog.postman.com
  • схема — https
  • порт — 3000 (обычно исключается и равен 80 для http или 443 для https).

Гибкость SOP для различных типов ресурсов:

  • Изображения: Веб-страницы могут встраивать изображения с любого источника без ограничений.
  • CSS стили: Внешние CSS могут загружаться на веб-страницу с использованием тега <link> или правил @import.
  • Скрипты: JavaScript файлы могут загружаться и выполняться с любого источника, но они могут получать доступ только к DOM-элементам из того же источника, где они были загружены.
  • Медиа файлы: Аудио и видео файлы могут быть встроены и воспроизводиться из различных источников с использованием тегов <audio> или <video>.
  • iframe: Встроенный <iframe> может загружать контент из любого источника, но политика SOP разделяет контент внутри iframe и контент родительской страницы.

🔓 ЧТО ТАКОЕ CORS (ОБМЕН РЕСУРСАМИ МЕЖДУ РАЗНЫМИ ИСТОЧНИКАМИ)?

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

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

‼️ ПРЕДВАРИТЕЛЬНЫЕ ЗАПРОСЫ

Предварительный запрос — это специальный запрос, который отправляется перед основным запросом к серверу. Он использует метод OPTIONS и содержит заголовки, сообщающие серверу о типе предстоящего запроса.

Если предварительный запрос не проходит, браузер не выполняет основной запрос и выводит ошибку CORS.

Важные заголовки предварительного запроса и ответа сервера

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

  • Access-Control-Request-Method — сообщает серверу, какой HTTP-метод будет использоваться в основном запросе.
  • Access-Control-Request-Headers — перечисляет заголовки, которые будут использоваться в основном запросе.
  • Origin — указывает домен, с которого будет сделан основной запрос.

Заголовки в ответе на предварительный запрос:

  • Access-Control-Allow-Origin — указывает, какие домены могут получать ответы.
  • Access-Control-Allow-Methods — перечисляет методы, разрешенные для кросс-доменных запросов.
  • Access-Control-Allow-Headers — указывает, какие заголовки разрешены в запросе.
  • Access-Control-Max-Age — показывает, как долго результат предварительного запроса может кэшироваться.

КОГДА ВЫПОЛНЯЕТСЯ ПРЕДВАРИТЕЛЬНЫЙ ЗАПРОС?

Браузер о��правляет "предварительный запрос" на сервер любой раз, когда происходит действие, изменяющее данные. Это необходимо, поскольку такие запросы считаются небезопасными, и браузер сначала проверяет, разрешает ли сервер CORS, прежде чем выполнять фактический запрос.

Предварительный запрос выполняется, если выполняется одно из следующих условий:

  1. Метод не "простой": не GET, HEAD, POST. Например: DELETE и PUT.
  2. POST с "непростым" Content-Type: любой, кроме application/x-www-form-urlencoded, multipart/form-data, text/plain.
  3. Используются пользовательские заголовки: например, Authorization, X-Requested-With, Content-Type: application/json.
  4. Ответ требует учетных данных (withCredentials: true в JS), и сервер не указывает Access-Control-Allow-Credentials: true.
  5. Запрос содержит пользовательские заголовки или методы, не включенные в стандарт.
  6. Запрос использует fetch() с параметрами, влияющими на CORS, например, с mode: 'cors' или пользовательским заголовком.

КЭШИРОВАНИЕ ОТВЕТОВ НА ПРЕДВАРИТЕЛЬНЫЕ ЗАПРОСЫ

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

❗️ ПРОСТЫЕ ЗАПРОСЫ

Это запросы CORS, которые отправляются как часть основного HTTP-запроса. Для того чтобы простой запрос был отправлен на сервер, необходимо выполнить следующие условия:

  • Запрос должен использовать один из следующих HTTP-методов: GET, HEAD или POST.
  • Заголовки запроса должны быть базовыми, которые обычно автоматически устанавливаются клиентом. Простые заголовки включают:
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type

Если запрос использует метод POST, заголовок Content-Type должен иметь одно из следующих значений:

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

🟢 ЗАГОЛОВКИ ОТВЕТА CORS

Сервер использует заголовки ответа для предоставления информации о CORS браузеру. Ответ на запрос CORS обычно содержит следующие заголовки:

  • Access-Control-Allow-Origin — сообщает браузеру, какие источники могут получить доступ к ресурсу. Может содержать имя источника (например, https://postman.com) или использовать символ подстановки *.
  • Access-Control-Allow-Credentials — булевое значение, указывающее, нужно ли браузеру включать учетные данные (куки или HTTP аутентификацию) при выполнении запроса с другого источника.
  • Access-Control-Allow-Methods — используется в ответе на предварительный запрос, чтобы указать разрешенные методы запросов в основном запросе (например, GET, POST, OPTIONS).
  • Access-Control-Allow-Headers — используется в ответе на предварительный запрос, чтобы указать разрешенные заголовки в основном запросе.
  • Access-Control-Max-Age — указывает максимальное время (в секундах), в течение которого ответ на предварительный запрос может кэшироваться.

Успешный ответ на запрос CORS может включать любой код статуса HTTP, но ответ на предварительный запрос должен указывать статус OK (т.е. 200 или 204).

🛡 ПРЕИМУЩЕСТВА РАБОТЫ С CORS

CORS улучшает общую безопасность приложения, обеспечивая при этом гибкость и совместимость. Основные преимущества:

  • Улучшенная безопасность API: CORS защищает от несанкционированных запросов к чувствительным ресурсам на другом домене.
  • Кросс-доменная аутентификация: CORS обеспечивает безопасную аутентификацию, контролируя передачу учетных данных в кросс-доменных запросах.
  • Стандартизация: CORS — это стандартизированный механизм, поддерживаемый всеми основными веб-браузерами.
  • Интеграция API: CORS необходим для интеграции веб-приложений с внешними API, позволяя разработчикам использовать функциональность сторонних сервисов.

👍 ЛУЧШИЕ ПРАКТИКИ ДЛЯ РЕАЛИЗАЦИИ CORS

Хотя CORS был разработан для безопасного изменения политики одинакового источника, его необходимо правильно реализовывать. Неправильная реализация CORS может привести к рискам для безопасности данных. Поэтому важно следовать лучшим практикам, таким как:

  • Избегайте «отравления кеша»: Убедитесь, что заголовок Access-Control-Max-Age установлен на разумный период (рекомендуется 5 секунд).
  • Будьте конкретными: Не используйте знаки подстановки * без необходимости. Явно указывайте источники, имеющие доступ к ресурсам сервера.
  • Добавляйте важные заголовки CORS: Убедитесь, что все необходимые заголовки включены в ответ сервера.
  • Документируйте все: Важно правильно документировать политики CORS, чтобы они были понятны разработчикам.
  • Проверка учетных данных: По умолчанию CORS игнорирует учетные данные. Если нужно включить учетные данные в кросс-доменные запросы, установите Access-Control-Allow-Credentials в true.

Leave a Reply

Ваш адрес email не будет опубликован. Обязательные поля помечены *