Що таке CORS і як налаштувати доступ?
Автор: Редакція Domeny.ForSale Опубліковано
Коротко: 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 без додаткових заголовків часто не потребує попереднього запиту.
Файли cookie та зірочка: дві важливі пастки
Публічний 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
В інструментах розробника браузера відкрийте вкладку мережевих запитів і виконайте чотири перевірки:
- Порівняйте протокол, хост і порт сторінки та API.
- Перевірте, чи є запит
OPTIONSі чи сервер правильно на нього відповідає. - Перегляньте заголовки відповіді на основний запит, зокрема й у разі помилки сервера.
- Перевірте, чи дозвіл охоплює використані метод, заголовки та, за потреби, облікові дані.
Те, що виклик працює в curl, не доводить правильності налаштування CORS: цей інструмент не застосовує обмежень браузера. Налаштування mode: 'no-cors' також не розблокує читання JSON — воно дає непрозору відповідь, вміст якої недоступний скрипту.
CORS не замінює входу в обліковий запис, перевірки прав доступу чи захисту від CSRF — примусового виконання небажаних дій через інший сайт. Частина запитів може дійти до сервера, навіть якщо читання відповіді заблоковано. Почніть виправлення з налаштування API або посередника, який ви контролюєте, а не з вимкнення захисту браузера.
Дивіться також: Що таке API?
