Вичерпний посібник з розуміння та впровадження Cross-Origin Resource Sharing (CORS) для безпечної комунікації JavaScript між різними доменами.
Реалізація міждоменної безпеки: найкращі практики для JavaScript комунікації
У сучасному взаємопов'язаному вебі JavaScript-додаткам часто потрібно взаємодіяти з ресурсами з різних джерел (доменів, протоколів або портів). Ця взаємодія регулюється політикою однакового походження (Same-Origin Policy) браузера — ключовим механізмом безпеки, призначеним для запобігання доступу шкідливих скриптів до конфіденційних даних через межі доменів. Однак легітимна міждоменна комунікація часто є необхідною. Саме тут у гру вступає Cross-Origin Resource Sharing (CORS). Ця стаття надає вичерпний огляд CORS, його реалізації та найкращих практик для безпечної міждоменної комунікації в JavaScript.
Розуміння політики однакового походження
Політика однакового походження (Same-Origin Policy, SOP) — це фундаментальна концепція безпеки у веб-браузерах. Вона обмежує скрипти, що виконуються на одному джерелі (origin), у доступі до ресурсів з іншого джерела. Джерело визначається комбінацією протоколу (наприклад, HTTP або HTTPS), доменного імені (наприклад, example.com) та номера порту (наприклад, 80 або 443). Дві URL-адреси мають однакове походження, тільки якщо всі три компоненти точно збігаються.
Наприклад:
http://www.example.comтаhttp://www.example.com/path: Однакове походженняhttp://www.example.comтаhttps://www.example.com: Різне походження (різний протокол)http://www.example.comтаhttp://subdomain.example.com: Різне походження (різний домен)http://www.example.com:80таhttp://www.example.com:8080: Різне походження (різний порт)
SOP є критично важливим захистом від атак міжсайтового скриптингу (Cross-Site Scripting, XSS), під час яких шкідливі скрипти, впроваджені на вебсайт, можуть викрадати дані користувачів або виконувати несанкціоновані дії від їхнього імені.
Що таке Cross-Origin Resource Sharing (CORS)?
CORS — це механізм, який використовує HTTP-заголовки, щоб дозволити серверам вказувати, яким джерелам (доменам, схемам або портам) дозволено отримувати доступ до їхніх ресурсів. По суті, він послаблює політику однакового походження для конкретних міждоменних запитів, уможливлюючи легітимну комунікацію, але водночас захищаючи від зловмисних атак.
CORS працює шляхом додавання нових HTTP-заголовків, які визначають дозволені джерела та методи (наприклад, GET, POST, PUT, DELETE), що дозволені для міждоменних запитів. Коли браузер робить міждоменний запит, він надсилає заголовок Origin разом із запитом. Сервер відповідає заголовком Access-Control-Allow-Origin, який вказує дозволене джерело (або джерела). Якщо джерело запиту відповідає значенню в заголовку Access-Control-Allow-Origin (або якщо значенням є *), браузер дозволяє коду JavaScript отримати доступ до відповіді.
Як працює CORS: детальне пояснення
Процес CORS зазвичай включає два типи запитів:
- Прості запити: Це запити, які відповідають певним критеріям. Якщо запит відповідає цим умовам, браузер надсилає його безпосередньо.
- Preflighted-запити: Це складніші запити, які вимагають від браузера спочатку надіслати "preflight"-запит OPTIONS на сервер, щоб визначити, чи безпечно надсилати фактичний запит.
1. Прості запити
Запит вважається "простим", якщо він відповідає всім наступним умовам:
- Метод —
GET,HEADабоPOST. - Якщо метод —
POST, заголовокContent-Typeє одним із наступних: application/x-www-form-urlencodedmultipart/form-datatext/plain- Не встановлено жодних користувацьких заголовків.
Приклад простого запиту:
GET /resource HTTP/1.1
Origin: http://www.example.com
Приклад відповіді сервера, що дозволяє джерело:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://www.example.com
Content-Type: application/json
{
"data": "Some data"
}
Якщо заголовок Access-Control-Allow-Origin присутній і його значення збігається з джерелом запиту або встановлено як *, браузер дозволяє скрипту отримати доступ до даних відповіді. В іншому випадку браузер блокує доступ до відповіді, і в консолі відображається повідомлення про помилку.
2. Preflighted-запити
Запит вважається "preflighted", якщо він не відповідає критеріям простого запиту. Це зазвичай трапляється, коли запит використовує інший метод HTTP (наприклад, PUT, DELETE), встановлює користувацькі заголовки або використовує Content-Type, відмінний від дозволених значень.
Перед надсиланням фактичного запиту браузер спочатку надсилає запит OPTIONS на сервер. Цей "preflight"-запит містить такі заголовки:
Origin: Джерело сторінки, що робить запит.Access-Control-Request-Method: Метод HTTP, який буде використаний у фактичному запиті (наприклад,PUT,DELETE).Access-Control-Request-Headers: Список користувацьких заголовків, розділених комами, які будуть надіслані у фактичному запиті.
Приклад preflight-запиту:
OPTIONS /resource HTTP/1.1
Origin: http://www.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header, Content-Type
Сервер повинен відповісти на запит OPTIONS з такими заголовками:
Access-Control-Allow-Origin: Джерело, якому дозволено робити запит (або*, щоб дозволити будь-яке джерело).Access-Control-Allow-Methods: Список HTTP-методів, розділених комами, які дозволені для міждоменних запитів (наприклад,GET,POST,PUT,DELETE).Access-Control-Allow-Headers: Список користувацьких заголовків, розділених комами, які дозволено надсилати в запиті.Access-Control-Max-Age: Кількість секунд, протягом яких відповідь на preflight-запит може кешуватися браузером.
Приклад відповіді сервера на preflight-запит:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://www.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: X-Custom-Header, Content-Type
Access-Control-Max-Age: 86400
Якщо відповідь сервера на preflight-запит вказує, що фактичний запит дозволений, браузер надішле фактичний запит. В іншому випадку браузер заблокує запит і відобразить повідомлення про помилку.
Реалізація CORS на стороні сервера
CORS в основному реалізується на стороні сервера шляхом встановлення відповідних HTTP-заголовків у відповіді. Конкретні деталі реалізації залежать від технології, що використовується на сервері.
Приклад з використанням Node.js та Express:
const express = require('express');
const cors = require('cors');
const app = express();
// Увімкнути CORS для всіх джерел
app.use(cors());
// Альтернативно, налаштувати CORS для конкретних джерел
// const corsOptions = {
// origin: 'http://www.example.com'
// };
// app.use(cors(corsOptions));
app.get('/resource', (req, res) => {
res.json({ message: 'This is a CORS-enabled resource' });
});
app.listen(3000, () => {
console.log('Server listening on port 3000');
});
Проміжне програмне забезпечення cors спрощує процес встановлення заголовків CORS в Express. Ви можете увімкнути CORS для всіх джерел за допомогою cors() або налаштувати його для конкретних джерел за допомогою cors(corsOptions).
Приклад з використанням Python та Flask:
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
@app.route("/resource")
def hello():
return {"message": "This is a CORS-enabled resource"}
if __name__ == '__main__':
app.run(debug=True)
Розширення flask_cors надає простий спосіб увімкнути CORS у додатках Flask. Ви можете увімкнути CORS для всіх джерел, передавши app у CORS(). Також можливе налаштування для конкретних джерел.
Приклад з використанням Java та Spring Boot:
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("/resource")
.allowedOrigins("http://www.example.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("Content-Type", "X-Custom-Header")
.allowCredentials(true)
.maxAge(3600);
}
}
У Spring Boot ви можете налаштувати CORS за допомогою WebMvcConfigurer. Це дозволяє детально контролювати дозволені джерела, методи, заголовки та інші налаштування CORS.
Встановлення заголовків CORS напряму (загальний приклад)
Якщо ви не використовуєте жодного фреймворку, ви можете встановити заголовки безпосередньо у вашому серверному коді (наприклад, PHP, Ruby on Rails тощо):
Найкращі практики CORS
Щоб забезпечити безпечну та ефективну міждоменну комунікацію, дотримуйтесь цих найкращих практик:
- Уникайте використання
Access-Control-Allow-Origin: *у продакшені: Дозвіл усім джерелам отримувати доступ до ваших ресурсів може становити загрозу безпеці. Натомість вказуйте конкретні дозволені джерела. - Використовуйте HTTPS: Завжди використовуйте HTTPS як для джерела запиту, так і для сервера, щоб захистити дані під час передачі.
- Перевіряйте вхідні дані: Завжди перевіряйте та санітизуйте дані, отримані з міждоменних запитів, щоб запобігти атакам ін'єкцій.
- Впроваджуйте належну автентифікацію та авторизацію: Переконайтеся, що тільки авторизовані користувачі можуть отримувати доступ до конфіденційних ресурсів.
- Кешуйте preflight-відповіді: Використовуйте
Access-Control-Max-Age, щоб кешувати preflight-відповіді та зменшити кількість запитівOPTIONS. - Розгляньте можливість використання облікових даних: Якщо ваш API вимагає автентифікації за допомогою файлів cookie або HTTP-автентифікації, вам потрібно встановити заголовок
Access-Control-Allow-Credentialsнаtrueна сервері та параметрcredentialsна'include'у вашому JavaScript-коді (наприклад, при використанніfetchабоXMLHttpRequest). Будьте надзвичайно обережні при використанні цієї опції, оскільки вона може створити вразливості безпеки, якщо її неправильно обробити. Також, коли Access-Control-Allow-Credentials встановлено в true, Access-Control-Allow-Origin не може бути встановлено в "*". Ви повинні явно вказати дозволене джерело (джерела). - Регулярно переглядайте та оновлюйте конфігурацію CORS: У міру розвитку вашого додатка регулярно переглядайте та оновлюйте конфігурацію CORS, щоб переконатися, що вона залишається безпечною та відповідає вашим потребам.
- Розумійте наслідки різних конфігурацій CORS: Будьте в курсі наслідків для безпеки різних конфігурацій CORS і вибирайте конфігурацію, яка підходить для вашого додатка.
- Тестуйте вашу реалізацію CORS: Ретельно тестуйте вашу реалізацію CORS, щоб переконатися, що вона працює як очікувалося і не створює жодних вразливостей безпеки. Використовуйте інструменти розробника в браузері для перевірки мережевих запитів та відповідей, а також використовуйте інструменти автоматизованого тестування для перевірки поведінки CORS.
Приклад: використання Fetch API з CORS
Ось приклад того, як використовувати fetch API для виконання міждоменного запиту:
fetch('https://api.example.com/data', {
method: 'GET',
mode: 'cors', // Повідомляє браузеру, що це CORS-запит
headers: {
'Content-Type': 'application/json',
'X-Custom-Header': 'value'
}
})
.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('There was a problem with the fetch operation:', error);
});
Опція mode: 'cors' повідомляє браузеру, що це CORS-запит. Якщо сервер не дозволяє джерело, браузер заблокує доступ до відповіді, і буде згенеровано помилку.
Якщо ви використовуєте облікові дані (наприклад, файли cookie), вам потрібно встановити опцію credentials на 'include':
fetch('https://api.example.com/data', {
method: 'GET',
mode: 'cors',
credentials: 'include', // Включити файли cookie в запит
headers: {
'Content-Type': 'application/json'
}
})
.then(response => {
// ...
});
CORS та JSONP
JSON з доповненням (JSON with Padding, JSONP) — це стара техніка для обходу політики однакового походження. Вона працює шляхом динамічного створення тегу <script>, який завантажує дані з іншого домену. Хоча JSONP може бути корисним у певних ситуаціях, він має значні обмеження безпеки, і його слід уникати, коли це можливо. CORS є кращим рішенням для міждоменної комунікації, оскільки він надає більш безпечний та гнучкий механізм.
Ключові відмінності між CORS та JSONP:
- Безпека: CORS є безпечнішим за JSONP, оскільки він дозволяє серверу контролювати, яким джерелам дозволено отримувати доступ до його ресурсів. JSONP не надає жодного контролю над джерелами.
- HTTP-методи: CORS підтримує всі HTTP-методи (наприклад,
GET,POST,PUT,DELETE), тоді як JSONP підтримує лишеGET-запити. - Обробка помилок: CORS забезпечує кращу обробку помилок, ніж JSONP. Коли запит CORS зазнає невдачі, браузер надає детальні повідомлення про помилки. Обробка помилок у JSONP обмежена виявленням того, чи успішно завантажився скрипт.
Вирішення проблем з CORS
Проблеми з CORS можуть бути складними для налагодження. Ось кілька поширених порад щодо їх вирішення:
- Перевірте консоль браузера: Консоль браузера зазвичай надає детальні повідомлення про помилки, пов'язані з CORS.
- Проаналізуйте мережеві запити: Використовуйте інструменти розробника у браузері для перевірки HTTP-заголовків як запиту, так і відповіді. Переконайтеся, що заголовки
OriginтаAccess-Control-Allow-Originвстановлені правильно. - Перевірте конфігурацію на стороні сервера: Ретельно перевірте вашу серверну конфігурацію CORS, щоб переконатися, що вона дозволяє правильні джерела, методи та заголовки.
- Очистіть кеш браузера: Іноді кешовані preflight-відповіді можуть спричиняти проблеми з CORS. Спробуйте очистити кеш браузера або використати режим приватного перегляду.
- Використовуйте CORS-проксі: У деяких випадках вам може знадобитися використовувати CORS-проксі для обходу обмежень CORS. Однак майте на увазі, що використання CORS-проксі може нести ризики для безпеки.
- Перевірте наявність помилок у конфігурації: Шукайте поширені помилки, такі як відсутність заголовка
Access-Control-Allow-Origin, неправильні значенняAccess-Control-Allow-MethodsабоAccess-Control-Allow-Headers, або неправильний заголовокOriginу запиті.
Висновок
Cross-Origin Resource Sharing (CORS) є важливим механізмом для забезпечення безпечної міждоменної комунікації в JavaScript-додатках. Розуміючи політику однакового походження, робочий процес CORS та різноманітні пов'язані з ним HTTP-заголовки, розробники можуть ефективно впроваджувати CORS для захисту своїх додатків від вразливостей безпеки, дозволяючи при цьому легітимні міждоменні запити. Дотримання найкращих практик для конфігурації CORS та регулярний перегляд вашої реалізації є вирішальними для підтримки безпечного та надійного веб-додатка.
Цей вичерпний посібник надає міцну основу для розуміння та впровадження CORS. Не забувайте звертатися до офіційної документації та ресурсів для вашої конкретної серверної технології, щоб переконатися, що ви реалізуєте CORS правильно та безпечно.