Розкрийте таємниці CORS (Cross-Origin Resource Sharing) і дізнайтеся, як безпечно вмикати міждоменні запити у ваших вебзастосунках. Цей вичерпний посібник охоплює все, від основ до розширених конфігурацій, забезпечуючи безперебійний та безпечний зв'язок між різними джерелами.
Демістифікація CORS: вичерпний посібник з Cross-Origin Resource Sharing
У сучасному взаємопов'язаному вебі застосункам часто потрібно отримувати доступ до ресурсів з різних джерел. Саме тут у гру вступає Cross-Origin Resource Sharing (CORS). CORS — це ключовий механізм безпеки, який регулює, як веббраузери обробляють запити від одного джерела (домен, протокол і порт) до іншого. Розуміння CORS є важливим для кожного веброзробника, щоб створювати безпечні та функціональні вебзастосунки.
Що таке політика однакового походження (Same-Origin Policy)?
Перш ніж заглиблюватися в CORS, важливо зрозуміти політику однакового походження (Same-Origin Policy, SOP). SOP — це фундаментальний механізм безпеки, реалізований у веббраузерах. Його мета — запобігти доступу шкідливих скриптів з одного вебсайту до конфіденційних даних на іншому. Джерело визначається комбінацією протоколу (наприклад, HTTP або HTTPS), домену (наприклад, example.com) та номера порту (наприклад, 80 або 443). Дві URL-адреси вважаються такими, що мають однакове походження, якщо вони мають однаковий протокол, домен і порт.
Приклад:
http://example.com/app1
таhttp://example.com/app2
- Однакове походження (однаковий протокол, домен і порт)https://example.com/app1
таhttp://example.com/app1
- Різне походження (різний протокол)http://example.com:8080/app1
таhttp://example.com/app1
- Різне походження (різний порт)http://sub.example.com/app1
таhttp://example.com/app1
- Різне походження (різний субдомен – вважається іншим доменом)
SOP обмежує доступ скриптів до ресурсів з іншого джерела, якщо не вжито спеціальних заходів, таких як CORS, щоб це дозволити.
Чому CORS необхідний?
Хоча політика однакового походження є життєво важливою для безпеки, вона також може бути обмежувальною. Багато сучасних вебзастосунків покладаються на отримання даних з різних серверів, таких як API або мережі доставки контенту (CDN). CORS надає контрольований спосіб послабити SOP і дозволити легітимні міждоменні запити, зберігаючи при цьому безпеку.
Розглянемо сценарій, коли вебзастосунок, розміщений на http://example.com
, повинен отримати дані з API-сервера, розміщеного на http://api.example.net
. Без CORS браузер заблокував би цей запит через SOP. CORS дозволяє API-серверу явно вказати, яким джерелам дозволено доступ до його ресурсів, що дає змогу вебзастосунку функціонувати належним чином.
Як працює CORS: основи
CORS працює через серію HTTP-заголовків, якими обмінюються клієнт (браузер) і сервер. Сервер використовує ці заголовки, щоб повідомити браузеру, чи дозволено йому доступ до запитуваного ресурсу. Ключовим HTTP-заголовком є Access-Control-Allow-Origin
.
Сценарій 1: Простий запит
«Простий запит» — це запит GET, HEAD або POST, який відповідає певним критеріям (наприклад, заголовок Content-Type
є одним із application/x-www-form-urlencoded
, multipart/form-data
або text/plain
). У цьому випадку браузер надсилає запит безпосередньо на сервер, а сервер відповідає із заголовком Access-Control-Allow-Origin
.
Запит клієнта (з http://example.com):
GET /data HTTP/1.1
Host: api.example.net
Origin: http://example.com
Відповідь сервера (з http://api.example.net):
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://example.com
Content-Type: application/json
{
"data": "Деякі дані з сервера"
}
У цьому прикладі сервер відповідає Access-Control-Allow-Origin: http://example.com
, вказуючи, що запити з http://example.com
дозволені. Якщо джерело в запиті не відповідає значенню в заголовку Access-Control-Allow-Origin
(або якщо заголовок відсутній), браузер заблокує відповідь і не дозволить клієнтському скрипту отримати доступ до даних.
Сценарій 2: Передзапит (для складних запитів)
Для складніших запитів, наприклад, тих, що використовують методи HTTP, такі як PUT, DELETE, або тих, що мають власні заголовки, браузер виконує «передзапит» за допомогою методу HTTP OPTIONS. Цей передзапит запитує у сервера дозвіл перед надсиланням фактичного запиту. Сервер відповідає заголовками, які вказують, які методи, заголовки та джерела дозволені.
Передзапит клієнта (з http://example.com):
OPTIONS /data HTTP/1.1
Host: api.example.net
Origin: http://example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Відповідь сервера (з http://api.example.net):
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://example.com
Access-Control-Allow-Methods: GET, PUT, DELETE
Access-Control-Allow-Headers: X-Custom-Header, Content-Type
Access-Control-Max-Age: 3600
Пояснення заголовків:
Access-Control-Allow-Origin: http://example.com
- Вказує, що запити зhttp://example.com
дозволені.Access-Control-Allow-Methods: GET, PUT, DELETE
- Визначає методи HTTP, дозволені для міждоменних запитів.Access-Control-Allow-Headers: X-Custom-Header, Content-Type
- Перелічує дозволені власні заголовки у фактичному запиті.Access-Control-Max-Age: 3600
- Визначає тривалість (у секундах), протягом якої відповідь на передзапит може кешуватися браузером. Це допомагає зменшити кількість передзапитів.
Якщо відповідь сервера на передзапит вказує, що запит дозволено, браузер переходить до фактичного запиту. В іншому випадку браузер блокує запит.
Фактичний запит клієнта (з http://example.com):
PUT /data HTTP/1.1
Host: api.example.net
Origin: http://example.com
X-Custom-Header: some-value
Content-Type: application/json
{
"data": "Деякі дані для оновлення"
}
Відповідь сервера (з http://api.example.net):
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://example.com
Content-Type: application/json
{
"status": "Дані успішно оновлено"
}
Поширені заголовки CORS
Ось розбір ключових заголовків CORS, які вам потрібно розуміти:
Access-Control-Allow-Origin
: Цей заголовок є найфундаментальнішим. Він вказує джерело(а), яким дозволено доступ до ресурсу. Можливі значення:- Конкретне джерело (наприклад,
http://example.com
). *
(wildcard): Це дозволяє запити з будь-якого джерела. Використовуйте з обережністю, оскільки це може поставити під загрозу безпеку, якщо йдеться про конфіденційні дані. Зазвичай цього слід уникати в продакшен-середовищах.
- Конкретне джерело (наприклад,
Access-Control-Allow-Methods
: Цей заголовок визначає методи HTTP (наприклад, GET, POST, PUT, DELETE), дозволені для міждоменних запитів. Він використовується у відповіді на передзапит.Access-Control-Allow-Headers
: Цей заголовок перелічує власні заголовки, дозволені в міждоменних запитах. Він також використовується у відповіді на передзапит.Access-Control-Allow-Credentials
: Цей заголовок вказує, чи дозволяє сервер включати облікові дані (наприклад, файли cookie, заголовки авторизації) в міждоменні запити. Його слід встановити вtrue
, якщо вам потрібно надсилати облікові дані. На стороні клієнта вам також потрібно встановитиwithCredentials = true
для об'єкта XMLHttpRequest.Access-Control-Expose-Headers
: За замовчуванням браузери надають клієнтським скриптам доступ лише до обмеженого набору заголовків відповіді (наприклад,Cache-Control
,Content-Language
,Content-Type
,Expires
,Last-Modified
,Pragma
). Якщо ви хочете надати доступ до інших заголовків, вам потрібно перелічити їх у заголовкуAccess-Control-Expose-Headers
.Access-Control-Max-Age
: Цей заголовок визначає максимальний час (у секундах), протягом якого браузер може кешувати передзапит. Більше значення зменшує кількість передзапитів, покращуючи продуктивність.
CORS у різних серверних мовах
Реалізація CORS зазвичай передбачає налаштування вашого серверного застосунку для надсилання відповідних заголовків CORS. Ось приклади того, як це зробити в різних мовах та фреймворках:
Node.js з Express
Ви можете використовувати пакет middleware cors
:
const express = require('express');
const cors = require('cors');
const app = express();
// Увімкнути CORS для всіх джерел (ВИКОРИСТОВУЙТЕ З ОБЕРЕЖНІСТЮ В ПРОДАКШЕНІ)
app.use(cors());
// Або налаштуйте CORS для конкретних джерел
// app.use(cors({
// origin: 'http://example.com'
// }));
app.get('/data', (req, res) => {
res.json({ message: 'CORS увімкнено для всіх джерел!' });
});
app.listen(3000, () => {
console.log('Сервер запущено на порту 3000');
});
Python з Flask
Ви можете використовувати розширення Flask-CORS
:
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
# Або налаштуйте CORS для конкретних джерел
# CORS(app, resources={r"/api/*": {"origins": "http://example.com"}})
@app.route("/data")
def hello():
return {"message": "CORS увімкнено для всіх джерел!"}
if __name__ == '__main__':
app.run(debug=True)
Java зі Spring Boot
Ви можете налаштувати CORS у вашому застосунку Spring Boot за допомогою анотацій або класів конфігурації:
З використанням анотацій:
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@CrossOrigin(origins = "http://example.com") // Дозволити запити з http://example.com
public class DataController {
@GetMapping("/data")
public String getData() {
return "CORS увімкнено для http://example.com!";
}
}
З використанням конфігурації:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/data")
.allowedOrigins("http://example.com") // Дозволити запити з http://example.com
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*");
}
}
PHP
"CORS увімкнено для http://example.com!");
echo json_encode($data);
?>
CORS та аспекти безпеки
Хоча CORS дозволяє міждоменні запити, важливо реалізувати його безпечно. Ось деякі важливі аспекти:
- Уникайте використання
*
дляAccess-Control-Allow-Origin
у продакшені: Це дозволяє запити з будь-якого джерела, що може становити ризик для безпеки. Натомість явно вказуйте джерела, яким дозволено доступ до ваших ресурсів. - Перевіряйте заголовок
Origin
на стороні сервера: Навіть якщо ви використовуєте фреймворк, який обробляє конфігурацію CORS, хорошою практикою є перевірка заголовкаOrigin
на стороні сервера, щоб переконатися, що запит надходить з очікуваного джерела. - Будьте уважні з
Access-Control-Allow-Credentials
: Якщо ви використовуєте облікові дані (наприклад, файли cookie, заголовки авторизації), переконайтеся, що на стороні сервера встановленоAccess-Control-Allow-Credentials: true
, а на стороні клієнта —withCredentials = true
. Однак майте на увазі, що використанняAccess-Control-Allow-Origin: *
не дозволено, колиAccess-Control-Allow-Credentials
встановлено вtrue
. Ви повинні явно вказати дозволені джерела. - Правильно налаштовуйте
Access-Control-Allow-Methods
таAccess-Control-Allow-Headers
: Дозволяйте лише ті методи HTTP та заголовки, які необхідні для правильної роботи вашого застосунку. Це допомагає зменшити поверхню атаки. - Використовуйте HTTPS: Завжди використовуйте HTTPS для своїх вебзастосунків та API для захисту даних під час передачі.
Вирішення проблем з CORS
Проблеми з CORS можуть бути складними для налагодження. Ось деякі поширені проблеми та способи їх вирішення:
- "No 'Access-Control-Allow-Origin' header is present on the requested resource": Це найпоширеніша помилка CORS. Вона означає, що сервер не надсилає заголовок
Access-Control-Allow-Origin
у своїй відповіді. Перевірте конфігурацію на стороні сервера, щоб переконатися, що заголовок надсилається правильно. - "Response to preflight request doesn't pass access control check: It does not have HTTP ok status": Ця помилка вказує на те, що передзапит не вдався. Це може статися, якщо сервер не налаштований на обробку запитів OPTIONS або якщо заголовки
Access-Control-Allow-Methods
чиAccess-Control-Allow-Headers
налаштовані неправильно. - "The value of the 'Access-Control-Allow-Origin' header in the response is not equal to the origin in the request": Ця помилка означає, що джерело в запиті не відповідає значенню в заголовку
Access-Control-Allow-Origin
. Переконайтеся, що сервер надсилає правильне джерело у відповіді. - Кешування в браузері: Іноді браузери можуть кешувати відповіді CORS, що може призвести до несподіваної поведінки. Спробуйте очистити кеш браузера або використати інший браузер, щоб перевірити, чи це вирішить проблему. Ви також можете використовувати заголовок
Access-Control-Max-Age
, щоб контролювати, як довго браузер кешує відповідь на передзапит.
Інструменти для налагодження:
- Інструменти розробника в браузері: Використовуйте інструменти розробника в браузері (зазвичай викликаються натисканням F12) для перевірки мережевих запитів та відповідей. Шукайте заголовки, пов'язані з CORS, та повідомлення про помилки.
- Онлайн-перевірники CORS: Існують онлайн-інструменти, які можуть допомогти вам протестувати вашу конфігурацію CORS. Ці інструменти надсилають запит на ваш сервер і аналізують заголовки відповіді для виявлення потенційних проблем.
Просунуті сценарії CORS
Хоча базові концепції CORS є відносно простими, існують і більш просунуті сценарії, які варто розглянути:
- CORS з субдоменами: Якщо вам потрібно дозволити запити з кількох субдоменів (наприклад,
app1.example.com
,app2.example.com
), ви не можете просто використовувати wildcard, як-от*.example.com
, у заголовкуAccess-Control-Allow-Origin
. Натомість вам доведеться динамічно генерувати заголовокAccess-Control-Allow-Origin
на основі заголовкаOrigin
у запиті. Не забувайте перевіряти джерело за білим списком дозволених субдоменів, щоб запобігти вразливостям безпеки. - CORS з кількома джерелами: Якщо вам потрібно дозволити запити з кількох конкретних джерел, ви не можете вказати кілька джерел у заголовку
Access-Control-Allow-Origin
(наприклад,Access-Control-Allow-Origin: http://example.com, http://another.com
є недійсним). Натомість вам доведеться динамічно генерувати заголовокAccess-Control-Allow-Origin
на основі заголовкаOrigin
у запиті. - CORS та CDN: При використанні CDN для обслуговування вашого API, вам потрібно налаштувати CDN так, щоб він пересилав заголовок
Origin
на ваш вихідний сервер і правильно кешував заголовокAccess-Control-Allow-Origin
. Зверніться до документації вашого CDN-провайдера для отримання конкретних інструкцій.
Найкращі практики CORS
Щоб забезпечити безпечну та ефективну реалізацію CORS, дотримуйтесь цих найкращих практик:
- Принцип найменших привілеїв: Дозволяйте лише мінімальний набір джерел, методів та заголовків, необхідних для коректної роботи вашого застосунку.
- Регулярно переглядайте конфігурацію CORS: У міру розвитку вашого застосунку регулярно переглядайте свою конфігурацію CORS, щоб переконатися, що вона все ще є доречною та безпечною.
- Використовуйте фреймворк або бібліотеку: Використовуйте існуючі фреймворки або бібліотеки, які надають вбудовану підтримку CORS. Це може спростити реалізацію та зменшити ризик помилок.
- Моніторте порушення CORS: Впровадьте моніторинг для виявлення та реагування на потенційні порушення CORS.
- Залишайтеся в курсі: Слідкуйте за останніми специфікаціями CORS та рекомендаціями з безпеки.
Висновок
CORS — це критично важливий механізм безпеки, який дозволяє контрольовані міждоменні запити у вебзастосунках. Розуміння того, як працює CORS і як його правильно налаштувати, є важливим для кожного веброзробника. Дотримуючись рекомендацій та найкращих практик, викладених у цьому вичерпному посібнику, ви зможете створювати безпечні та функціональні вебзастосунки, які безперешкодно взаємодіють з ресурсами з різних джерел.
Пам'ятайте, що завжди слід надавати пріоритет безпеці та уникати надмірно дозвільних конфігурацій CORS. Ретельно враховуючи наслідки для безпеки ваших налаштувань CORS, ви можете захистити свої застосунки та дані від несанкціонованого доступу.
Сподіваємося, цей посібник допоміг вам демістифікувати CORS. Щасливого кодингу!