Вичерпний посібник із посилення безпеки фронтенду за допомогою Content Security Policy (CSP) та Cross-Origin Resource Sharing (CORS) для захисту ваших вебзастосунків від сучасних загроз.
Посилення безпеки фронтенду: Content Security Policy та CORS
У сучасному взаємопов'язаному цифровому світі безпека фронтенду має першорядне значення. Вебзастосунки все частіше стають ціллю складних атак, що робить надійні заходи безпеки життєво необхідними. Два критично важливих компоненти безпечної архітектури фронтенду — це Content Security Policy (CSP) та Cross-Origin Resource Sharing (CORS). Цей вичерпний посібник надає глибокий огляд цих технологій, пропонуючи практичні приклади та дієві поради, які допоможуть вам зміцнити ваші вебзастосунки проти сучасних загроз.
Що таке Content Security Policy (CSP)?
Content Security Policy (CSP) — це додатковий рівень безпеки, який допомагає виявляти та пом'якшувати певні типи атак, зокрема міжсайтовий скриптинг (XSS) та атаки з ін'єкцією даних. CSP реалізується шляхом надсилання веб-сервером HTTP-заголовка відповіді Content-Security-Policy до браузера. Цей заголовок визначає білий список джерел, з яких браузеру дозволено завантажувати ресурси. Обмежуючи джерела контенту, які може завантажувати браузер, CSP значно ускладнює зловмисникам можливість впровадження шкідливого коду на ваш вебсайт.
Як працює CSP
CSP працює, наказуючи браузеру завантажувати ресурси (наприклад, скрипти, таблиці стилів, зображення, шрифти) лише з дозволених джерел. Ці джерела вказуються в заголовку CSP за допомогою директив. Якщо браузер намагається завантажити ресурс із джерела, яке не є явно дозволеним, він блокує запит і повідомляє про порушення.
Директиви CSP: вичерпний огляд
Директиви CSP контролюють типи ресурсів, які можна завантажувати з певних джерел. Ось огляд деяких з найважливіших директив:
- default-src: Визначає джерело за замовчуванням для всіх типів контенту. Це резервна директива, яка застосовується, коли інші, більш специфічні директиви, відсутні.
- script-src: Визначає джерела, з яких можна завантажувати скрипти. Це критично важливо для запобігання атакам XSS.
- style-src: Визначає джерела, з яких можна завантажувати таблиці стилів.
- img-src: Визначає джерела, з яких можна завантажувати зображення.
- font-src: Визначає джерела, з яких можна завантажувати шрифти.
- media-src: Визначає джерела, з яких можна завантажувати аудіо та відео.
- object-src: Визначає джерела, з яких можна завантажувати плагіни (наприклад, Flash). Часто встановлюється в 'none', щоб повністю вимкнути плагіни через властиві їм ризики безпеки.
- frame-src: Визначає джерела, з яких можна завантажувати фрейми (наприклад, <iframe>).
- connect-src: Визначає URL-адреси, до яких користувацький агент може підключатися за допомогою скриптових інтерфейсів, таких як XMLHttpRequest, WebSocket та EventSource.
- base-uri: Визначає URL-адреси, які можна використовувати в елементі <base> документа.
- form-action: Визначає URL-адреси, на які можна надсилати дані форм.
- upgrade-insecure-requests: Наказує користувацькому агенту автоматично оновлювати незахищені запити (HTTP) до захищених (HTTPS).
- report-uri: Визначає URL-адресу, куди браузер повинен надсилати звіти про порушення CSP. Ця директива застаріла на користь `report-to`.
- report-to: Визначає назву групи звітування, визначену в заголовку `Report-To`, куди браузер повинен надсилати звіти про порушення CSP.
Ключові слова списку джерел CSP
У директивах CSP можна використовувати ключові слова списку джерел для визначення дозволених джерел. Ось деякі поширені ключові слова:
- 'self': Дозволяє ресурси з того ж джерела (схема та хост), що й документ.
- 'none': Забороняє ресурси з усіх джерел.
- 'unsafe-inline': Дозволяє використання вбудованих скриптів та стилів (наприклад, тегів <script> та атрибутів style). Використовуйте з особливою обережністю, оскільки це значно послаблює захист CSP від XSS.
- 'unsafe-eval': Дозволяє використання функцій динамічної оцінки коду, таких як
eval()таFunction(). Використовуйте з особливою обережністю, оскільки це створює значні ризики для безпеки. - 'unsafe-hashes': Дозволяє певні вбудовані обробники подій або теги <style>, які відповідають вказаному хешу. Потребує підтримки браузером. Використовуйте з обережністю.
- 'strict-dynamic': Вказує, що довіра, явно надана скрипту, наявному в розмітці (шляхом супроводу його nonce або хешем), повинна поширюватися на всі скрипти, завантажені цим кореневим скриптом.
- data: Дозволяє data: URI (наприклад, вбудовані зображення, закодовані в base64). Використовуйте з обережністю.
- https:: Дозволяє завантажувати ресурси через HTTPS з будь-якого домену.
- [hostname]: Дозволяє ресурси з певного домену (наприклад, example.com). Ви також можете вказати номер порту (наприклад, example.com:8080).
- [scheme]://[hostname]:[port]: Повністю кваліфікований URI, що дозволяє ресурси з вказаної схеми, хоста та порту.
Практичні приклади CSP
Розглянемо деякі практичні приклади заголовків CSP:
Приклад 1: Базовий CSP з 'self'
Ця політика дозволяє ресурси лише з того самого джерела:
Content-Security-Policy: default-src 'self'
Приклад 2: Дозвіл скриптів з певного домену
Ця політика дозволяє скрипти з вашого власного домену та довіреного CDN:
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com
Приклад 3: Вимкнення вбудованих скриптів та стилів
Ця політика забороняє вбудовані скрипти та стилі, що є сильним захистом від XSS:
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'
Важливо: Вимкнення вбудованих скриптів вимагає рефакторингу вашого HTML для перенесення вбудованих скриптів у зовнішні файли.
Приклад 4: Використання Nonce для вбудованих скриптів
Якщо вам необхідно використовувати вбудовані скрипти, використовуйте nonce (криптографічно випадкові, одноразові токени) для внесення конкретних блоків вбудованих скриптів до білого списку. Це безпечніше, ніж 'unsafe-inline'. Сервер повинен генерувати унікальний nonce для кожного запиту та включати його як у заголовок CSP, так і в тег <script>.
Content-Security-Policy: default-src 'self'; script-src 'nonce-r4nd0mN0nc3'; style-src 'self'
<script nonce="r4nd0mN0nc3"> console.log('Inline script'); </script>
Примітка: Не забувайте генерувати новий nonce для кожного запиту. Не використовуйте nonce повторно!
Приклад 5: Використання хешів для вбудованих стилів
Подібно до nonce, хеші можна використовувати для внесення до білого списку конкретних вбудованих блоків <style>. Це робиться шляхом генерації хешу SHA256, SHA384 або SHA512 вмісту стилю.
Content-Security-Policy: default-src 'self'; style-src 'sha256-HASHEDSTYLES'
<style sha256="HASHEDSTYLES"> body { background-color: #f0f0f0; } </style>
Примітка: Хеші менш гнучкі, ніж nonce, оскільки будь-яка зміна вмісту стилю призведе до недійсності хешу.
Приклад 6: Звітування про порушення CSP
Для моніторингу порушень CSP використовуйте директиву report-uri або report-to:
Content-Security-Policy: default-src 'self'; report-to csp-endpoint;
Вам також потрібно налаштувати заголовок Report-To. Заголовок Report-To визначає одну або кілька груп звітування, які вказують, куди і як слід надсилати звіти.
Report-To: {"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"https://example.com/csp-report"}]}
Тестування та розгортання CSP
Впровадження CSP вимагає ретельного планування та тестування. Почніть із суворої політики та поступово послаблюйте її за потреби. Використовуйте заголовок Content-Security-Policy-Report-Only для тестування вашої політики без блокування ресурсів. Цей заголовок повідомляє про порушення, не застосовуючи політику, що дозволяє виявити та виправити проблеми перед розгортанням політики в продакшені.
Content-Security-Policy-Report-Only: default-src 'self'; report-to csp-endpoint;
Аналізуйте звіти, згенеровані браузером, щоб виявити будь-які порушення та відповідно скоригувати свою політику. Коли ви переконаєтеся, що ваша політика працює правильно, розгорніть її за допомогою заголовка Content-Security-Policy.
Найкращі практики для CSP
- Почніть з default-src: Завжди визначайте
default-srcдля встановлення базової політики. - Будьте конкретними: Використовуйте специфічні директиви та ключові слова списку джерел, щоб обмежити сферу дії вашої політики.
- Уникайте 'unsafe-inline' та 'unsafe-eval': Ці ключові слова значно послаблюють CSP і їх слід уникати, коли це можливо.
- Використовуйте nonce або хеші для вбудованих скриптів та стилів: Якщо вам необхідно використовувати вбудовані скрипти або стилі, використовуйте nonce або хеші для внесення конкретних блоків коду до білого списку.
- Моніторте порушення CSP: Використовуйте директиву
report-uriабоreport-toдля моніторингу порушень CSP та відповідного коригування вашої політики. - Тестуйте ретельно: Використовуйте заголовок
Content-Security-Policy-Report-Onlyдля тестування вашої політики перед її розгортанням в продакшені. - Ітеруйте та вдосконалюйте: CSP — це не одноразове налаштування. Постійно моніторте та вдосконалюйте свою політику, щоб адаптуватися до змін у вашому застосунку та ландшафті загроз.
Що таке Cross-Origin Resource Sharing (CORS)?
Cross-Origin Resource Sharing (CORS) — це механізм, який дозволяє веб-сторінкам з одного джерела (домену) отримувати доступ до ресурсів з іншого джерела. За замовчуванням браузери застосовують політику того ж джерела (Same-Origin Policy), яка забороняє скриптам робити запити до іншого джерела, ніж те, з якого походить скрипт. CORS надає спосіб вибірково послабити це обмеження, дозволяючи легітимні міждоменні запити, одночасно захищаючи від зловмисних атак.
Розуміння політики того ж джерела (Same-Origin Policy)
Політика того ж джерела є фундаментальним механізмом безпеки, який запобігає доступу шкідливого скрипта з одного вебсайту до конфіденційних даних на іншому вебсайті. Джерело визначається схемою (протоколом), хостом (доменом) та портом. Дві URL-адреси мають однакове джерело тоді і тільки тоді, коли вони мають однакові схему, хост та порт.
Наприклад:
https://www.example.com/app1/index.htmlтаhttps://www.example.com/app2/index.htmlмають однакове джерело.https://www.example.com/index.htmlтаhttp://www.example.com/index.htmlмають різні джерела (різна схема).https://www.example.com/index.htmlтаhttps://sub.example.com/index.htmlмають різні джерела (різний хост).https://www.example.com:8080/index.htmlтаhttps://www.example.com:80/index.htmlмають різні джерела (різний порт).
Як працює CORS
Коли веб-сторінка робить міждоменний запит, браузер спочатку надсилає серверу "попередній" запит (preflight). Попередній запит використовує метод HTTP OPTIONS і містить заголовки, що вказують на метод HTTP та заголовки, які використовуватиме фактичний запит. Потім сервер відповідає заголовками, які вказують, чи дозволений міждоменний запит.
Якщо сервер дозволяє запит, він включає заголовок Access-Control-Allow-Origin у відповідь. Цей заголовок вказує джерело(а), яким дозволено доступ до ресурсу. Потім браузер продовжує виконувати фактичний запит. Якщо сервер не дозволяє запит, він не включає заголовок Access-Control-Allow-Origin, і браузер блокує запит.
Заголовки CORS: детальний огляд
CORS покладається на HTTP-заголовки для комунікації між браузером та сервером. Ось ключові заголовки CORS:
- Access-Control-Allow-Origin: Вказує джерело(а), яким дозволено доступ до ресурсу. Цей заголовок може містити конкретне джерело (наприклад,
https://www.example.com), символ підстановки (*) абоnull. Використання*дозволяє запити з будь-якого джерела, що зазвичай не рекомендується з міркувань безпеки. Використання `null` доречне лише для "непрозорих відповідей", наприклад, коли ресурс отримується за допомогою протоколу `file://` або data URI. - Access-Control-Allow-Methods: Вказує методи HTTP, які дозволені для міждоменного запиту (наприклад,
GET, POST, PUT, DELETE). - Access-Control-Allow-Headers: Вказує заголовки HTTP, які дозволені в міждоменному запиті. Це важливо для обробки користувацьких заголовків.
- Access-Control-Allow-Credentials: Вказує, чи повинен браузер включати облікові дані (наприклад, файли cookie, заголовки авторизації) в міждоменний запит. Цей заголовок повинен бути встановлений в
true, щоб дозволити облікові дані. - Access-Control-Expose-Headers: Вказує, які заголовки можуть бути доступні клієнту. За замовчуванням доступний лише обмежений набір заголовків.
- Access-Control-Max-Age: Вказує максимальний час (у секундах), протягом якого браузер може кешувати попередній запит.
- Origin: Це заголовок запиту, що надсилається браузером для вказівки джерела запиту.
- Vary: Загальний HTTP-заголовок, але важливий для CORS. Коли
Access-Control-Allow-Originгенерується динамічно, заголовокVary: Originповинен бути включений у відповідь, щоб інструктувати механізми кешування, що відповідь змінюється залежно від заголовка запитуOrigin.
Практичні приклади CORS
Розглянемо деякі практичні приклади конфігурацій CORS:
Приклад 1: Дозвіл запитів з певного джерела
Ця конфігурація дозволяє запити лише з https://www.example.com:
Access-Control-Allow-Origin: https://www.example.com
Приклад 2: Дозвіл запитів з будь-якого джерела (не рекомендується)
Ця конфігурація дозволяє запити з будь-якого джерела. Використовуйте з обережністю, оскільки це може створити ризики для безпеки:
Access-Control-Allow-Origin: *
Приклад 3: Дозвіл певних методів та заголовків
Ця конфігурація дозволяє методи GET, POST та PUT, а також заголовки Content-Type та Authorization:
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type, Authorization
Приклад 4: Дозвіл облікових даних
Щоб дозволити облікові дані (наприклад, файли cookie), вам потрібно встановити Access-Control-Allow-Credentials в true та вказати конкретне джерело (ви не можете використовувати *, дозволяючи облікові дані):
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Credentials: true
Вам також потрібно встановити credentials: 'include' у вашому запиті JavaScript fetch/XMLHttpRequest.
fetch('https://api.example.com/data', {
credentials: 'include'
})
Попередні запити CORS (Preflight Requests)
Для певних типів міждоменних запитів (наприклад, запитів з користувацькими заголовками або методами, відмінними від GET, HEAD або POST з Content-Type application/x-www-form-urlencoded, multipart/form-data або text/plain), браузер надсилає попередній запит, використовуючи метод OPTIONS. Сервер повинен відповісти на попередній запит відповідними заголовками CORS, щоб вказати, чи дозволений фактичний запит.
Ось приклад попереднього запиту та відповіді:
Попередній запит (OPTIONS):
OPTIONS /data HTTP/1.1
Origin: https://www.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type, Authorization
Попередня відповідь (200 OK):
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
Заголовок Access-Control-Max-Age вказує, як довго браузер може кешувати попередню відповідь, зменшуючи кількість попередніх запитів.
CORS та JSONP
JSON з доповненням (JSONP) — це стара техніка для обходу політики того ж джерела. Однак JSONP має значні ризики для безпеки, і його слід уникати на користь CORS. JSONP покладається на впровадження тегів <script> на сторінку, що може виконати довільний код. CORS надає більш безпечний та гнучкий спосіб обробки міждоменних запитів.
Найкращі практики для CORS
- Уникайте використання *: Уникайте використання символу підстановки (*) в заголовку
Access-Control-Allow-Origin, оскільки це дозволяє запити з будь-якого джерела. Замість цього вказуйте конкретне джерело(а), яким дозволено доступ до ресурсу. - Будьте конкретними з методами та заголовками: Вказуйте точні методи HTTP та заголовки, які дозволені в заголовках
Access-Control-Allow-MethodsтаAccess-Control-Allow-Headers. - Використовуйте Access-Control-Allow-Credentials з обережністю: Вмикайте
Access-Control-Allow-Credentialsлише тоді, коли вам потрібно дозволити облікові дані (наприклад, файли cookie) у міждоменних запитах. Будьте обізнані про наслідки для безпеки при дозволі облікових даних. - Захищайте свої попередні запити: Переконайтеся, що ваш сервер правильно обробляє попередні запити та повертає правильні заголовки CORS.
- Використовуйте HTTPS: Завжди використовуйте HTTPS як для джерела, так і для ресурсів, до яких ви звертаєтеся міждоменно. Це допомагає захиститися від атак типу "людина посередині".
- Vary: Origin: Якщо ви динамічно генеруєте заголовок `Access-Control-Allow-Origin`, завжди включайте заголовок `Vary: Origin`, щоб запобігти проблемам з кешуванням.
CSP та CORS на практиці: комбінований підхід
Хоча і CSP, і CORS вирішують проблеми безпеки, вони працюють на різних рівнях і забезпечують взаємодоповнюючий захист. CSP зосереджується на запобіганні завантаженню браузером шкідливого контенту, тоді як CORS контролює, які джерела можуть отримувати доступ до ресурсів на вашому сервері.
Поєднуючи CSP та CORS, ви можете створити більш надійну систему безпеки для ваших вебзастосунків. Наприклад, ви можете використовувати CSP для обмеження джерел, з яких можна завантажувати скрипти, та CORS для контролю доступу до ваших кінцевих точок API.
Приклад: Захист API за допомогою CSP та CORS
Припустимо, у вас є API, розміщений за адресою https://api.example.com, який ви хочете зробити доступним лише з https://www.example.com. Ви можете налаштувати ваш сервер так, щоб він повертав наступні заголовки:
Заголовки відповіді API (https://api.example.com):
Access-Control-Allow-Origin: https://www.example.com
Content-Type: application/json
І ви можете налаштувати ваш вебсайт (https://www.example.com) на використання наступного заголовка CSP:
Заголовок CSP вебсайту (https://www.example.com):
Content-Security-Policy: default-src 'self'; script-src 'self'; connect-src 'self' https://api.example.com;
Ця політика CSP дозволяє вебсайту завантажувати скрипти та підключатися до API, але забороняє йому завантажувати скрипти або підключатися до інших доменів.
Висновок
Content Security Policy (CSP) та Cross-Origin Resource Sharing (CORS) є важливими інструментами для посилення безпеки ваших фронтенд-застосунків. Ретельно налаштувавши CSP та CORS, ви можете значно знизити ризик атак XSS, атак з ін'єкцією даних та інших вразливостей безпеки. Не забувайте починати з суворої політики, ретельно тестувати та постійно моніторити й вдосконалювати вашу конфігурацію, щоб адаптуватися до змін у вашому застосунку та еволюціонуючому ландшафті загроз. Надаючи пріоритет безпеці фронтенду, ви можете захистити своїх користувачів та забезпечити цілісність ваших вебзастосунків у сучасному все більш складному цифровому світі.