Що таке CORS і як налаштувати доступ?

Автор: Опубліковано

Коротко: CORS (Cross-Origin Resource Sharing) — це механізм, за допомогою якого сервер може дозволити браузеру надати відповідь скрипту, що працює з іншим джерелом походження. Дозвіл задають заголовки відповіді сервера, а браузер стежить за їх дотриманням. Тож помилка CORS зазвичай означає проблему з дозволом на читання відповіді, а не обов’язково відсутність з’єднання із сервером.

Чому браузер обмежує доступ між адресами

Браузери застосовують політику однакового походження — same-origin policy. Вона обмежує можливість скрипту читати дані з іншого джерела походження. Завдяки цьому сторінка, відкрита в одній вкладці, не може вільно отримувати й читати приватні дані з інших сервісів, у яких користувач увійшов у свій обліковий запис.

Джерело походження (origin) — це сукупність трьох елементів: протоколу, імені хоста й порту. Зміна хоча б одного з них означає інше джерело походження. Шлях після імені хоста тут не має значення.

Для сторінки https://mahazyn.example порівняння виглядає так:

Цільова адресаТе саме джерело походження?Причина
https://mahazyn.example/tovaryТакЗмінюється лише шлях
https://api.mahazyn.exampleНіІнше ім’я хоста
http://mahazyn.exampleНіІнший протокол
https://sklep.example:8443НіПорт відрізняється від стандартного 443

CORS дає серверу змогу контрольовано послабити цю політику. Це не метод HTTP, як-от GET або POST, а механізм, що ґрунтується на заголовках HTTP.

Простий приклад: магазин отримує список товарів

Припустімо, що сторінка працює за адресою https://mahazyn.example, а API, тобто інтерфейс, який надає доступ до даних застосунку, — за адресою https://api.mahazyn.example. Скрипт сторінки хоче отримати товари:

fetch('https://api.sklep.example/produkty')
  .then(vidpovid => vidpovid.json())
  .then(tovary => console.log(tovary));

Оскільки адреси мають різні джерела походження, браузер додає до запиту заголовок, що вказує джерело походження сторінки:

Origin: https://sklep.example

Сервер API може повернути дані та заголовок із дозволом:

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

Браузер порівнює це значення з джерелом походження сторінки. Якщо дозвіл відповідає йому, браузер надає скрипту доступ до відповіді. Якщо заголовка немає або він указує іншу адресу, скрипт не може прочитати відповідь — навіть коли сервер повернув статус 200 OK.

Заголовок із дозволом має надходити від сервера API. Додавання Access-Control-Allow-Origin у коді запиту на боці браузера не розв’язує проблему.

Коли браузер спочатку надсилає OPTIONS

Не кожен запит між різними джерелами походження надсилається одразу. Для деяких браузер спочатку виконує preflight — попередній запит, який перевіряє дозвіл сервера.

Наприклад, надсилання замовлення методом POST із типом вмісту application/json спричиняє таку перевірку. Так само діє використання заголовка Authorization. Попередній запит може виглядати так:

OPTIONS /zamovlennia HTTP/1.1
Origin: https://sklep.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type

Сервер має відповісти дозволом, що охоплює джерело походження, метод і потрібні заголовки:

Access-Control-Allow-Origin: https://sklep.example
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type

Лише після успішної перевірки браузер надсилає основний запит POST. Відповідь на цей POST також має містити відповідний заголовок Access-Control-Allow-Origin.

Не кожен запит POST потребує preflight. Значення мають також заголовки й тип передаваного вмісту; звичайний GET без додаткових заголовків часто не потребує попереднього запиту.

Публічний API, відповіді якого мають бути доступні скриптам із будь-якого джерела походження, може повертати:

Access-Control-Allow-Origin: *

Проте зірочка не підходить для запитів, що надсилаються з файлами cookie в режимі врахування облікових даних. У такому разі код може використовувати credentials: 'include', а сервер має вказати конкретне дозволене джерело походження та повернути:

Access-Control-Allow-Origin: https://sklep.example
Access-Control-Allow-Credentials: true

Саме лише налаштування CORS не гарантує передавання файлів cookie. Діють також правила cookie, зокрема SameSite, та обмеження конфіденційності браузера.

Якщо сервер обслуговує кілька довірених джерел походження, він має перевіряти заголовок Origin за списком дозволених значень. Бездумне повернення будь-якого отриманого джерела походження разом із дозволом на використання облікових даних може відкрити доступ до приватних відповідей. Якщо відповідь залежить від Origin, слід також подбати про Vary: Origin, щоб кеш розрізняв варіанти відповіді.

Як діагностувати помилку CORS

В інструментах розробника браузера відкрийте вкладку мережевих запитів і виконайте чотири перевірки:

  1. Порівняйте протокол, хост і порт сторінки та API.
  2. Перевірте, чи є запит OPTIONS і чи сервер правильно на нього відповідає.
  3. Перегляньте заголовки відповіді на основний запит, зокрема й у разі помилки сервера.
  4. Перевірте, чи дозвіл охоплює використані метод, заголовки та, за потреби, облікові дані.

Те, що виклик працює в curl, не доводить правильності налаштування CORS: цей інструмент не застосовує обмежень браузера. Налаштування mode: 'no-cors' також не розблокує читання JSON — воно дає непрозору відповідь, вміст якої недоступний скрипту.

CORS не замінює входу в обліковий запис, перевірки прав доступу чи захисту від CSRF — примусового виконання небажаних дій через інший сайт. Частина запитів може дійти до сервера, навіть якщо читання відповіді заблоковано. Почніть виправлення з налаштування API або посередника, який ви контролюєте, а не з вимкнення захисту браузера.

Дивіться також: Що таке API?

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

Дізнайтеся, як PHP-FPM обробляє запити PHP, чим відрізняється від вебсервера та як налаштувати процеси під трафік і ресурси хостингу.
Дізнайтеся, як BIND обслуговує DNS, чим відрізняються його серверні ролі та коли варто розгортати його самостійно замість керованої послуги.
Дізнайтеся, що означають польський реєстр і номер REGON, чим вони відрізняються від NIP та KRS і як перевірити дані підприємства.