Изчерпателно ръководство за подобряване на frontend сигурността чрез CSP и CORS, което защитава уеб приложенията ви от съвременни заплахи.
Укрепване на сигурността на Frontend: Политика за сигурност на съдържанието и CORS
В днешния взаимосвързан дигитален свят сигурността на frontend е от първостепенно значение. Уеб приложенията все по-често стават мишена на сложни атаки, което прави стабилните мерки за сигурност съществени. Два критични компонента на сигурната frontend архитектура са Политиката за сигурност на съдържанието (CSP) и Споделянето на ресурси между източници (CORS). Това изчерпателно ръководство предоставя задълбочен поглед върху тези технологии, предлагайки практически примери и приложими съвети, които да ви помогнат да укрепите вашите уеб приложения срещу съвременните заплахи.
Какво е Политика за сигурност на съдържанието (CSP)?
Политиката за сигурност на съдържанието (CSP) е допълнителен слой на защита, който помага за откриването и смекчаването на определени видове атаки, включително Cross-Site Scripting (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': Позволява използването на вградени (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: Използване на Nonces за вградени скриптове
Ако трябва да използвате вградени скриптове, използвайте nonces (криптографски случайни, еднократни токени), за да добавите конкретни блокове с вградени скриптове в белия списък. Това е по-сигурно от '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 за всяка заявка. Не използвайте повторно nonces!
Пример 5: Използване на хешове за вградени стилове
Подобно на nonces, хешовете могат да се използват за добавяне на конкретни вградени <style> блокове в белия списък. Това се прави чрез генериране на SHA256, SHA384 или SHA512 хеш на съдържанието на стила.
Content-Security-Policy: default-src 'self'; style-src 'sha256-HASHEDSTYLES'
<style sha256="HASHEDSTYLES"> body { background-color: #f0f0f0; } </style>
Забележка: Хешовете са по-малко гъвкави от nonces, тъй като всяка промяна в съдържанието на стила ще направи хеша невалиден.
Пример 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 и трябва да се избягват, когато е възможно.
- Използвайте nonces или хешове за вградени скриптове и стилове: Ако трябва да използвате вградени скриптове или стилове, използвайте nonces или хешове, за да добавите конкретни кодови блокове в белия списък.
- Наблюдавайте нарушенията на CSP: Използвайте директивата
report-uriилиreport-to, за да наблюдавате нарушенията на CSP и да коригирате политиката си съответно. - Тествайте обстойно: Използвайте хедъра
Content-Security-Policy-Report-Only, за да тествате вашата политика, преди да я внедрите в производствена среда. - Итерирайте и усъвършенствайте: CSP не е еднократна конфигурация. Непрекъснато наблюдавайте и усъвършенствайте вашата политика, за да се адаптирате към промените във вашето приложение и променящата се среда на заплахи.
Какво е Споделяне на ресурси между източници (CORS)?
Споделянето на ресурси между източници (CORS) е механизъм, който позволява на уеб страници от един източник (домейн) да достъпват ресурси от различен източник. По подразбиране браузърите прилагат Политика за същия източник (Same-Origin Policy), която не позволява на скриптове да правят заявки към различен източник от този, от който произхожда скриптът. CORS предоставя начин за избирателно отпускане на това ограничение, позволявайки легитимни заявки между различни източници, като същевременно защитава от злонамерени атаки.
Разбиране на Политиката за същия източник
Политиката за същия източник е основен механизъм за сигурност, който предотвратява достъпа на злонамерен скрипт от един уебсайт до чувствителни данни на друг уебсайт. Източникът се дефинира от схемата (протокол), хоста (домейн) и порта. Два 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), заместващ символ (wildcard) (*) илиnull. Използването на*позволява заявки от всеки източник, което обикновено не се препоръчва от съображения за сигурност. Използването на `null` е подходящо само за „непрозрачни отговори“, като например когато ресурсът се извлича с помощта на протокола `file://` или data URI. - Access-Control-Allow-Methods: Посочва HTTP методите, които са разрешени за заявката от друг източник (напр.
GET, POST, PUT, DELETE). - Access-Control-Allow-Headers: Посочва HTTP хедърите, които са разрешени в заявката от друг източник. Това е важно за обработката на персонализирани хедъри.
- Access-Control-Allow-Credentials: Указва дали браузърът трябва да включва идентификационни данни (напр. бисквитки, хедъри за оторизация) в заявката от друг източник. Този хедър трябва да бъде настроен на
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: Разрешаване на идентификационни данни
За да разрешите идентификационни данни (напр. бисквитки), трябва да зададете 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'
})
Предварителни (Preflight) заявки в CORS
За определени типове заявки между източници (напр. заявки с персонализирани хедъри или методи, различни от 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 with Padding (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само ако трябва да разрешите идентификационни данни (напр. бисквитки) в заявки между различни източници. Бъдете наясно с последиците за сигурността от разрешаването на идентификационни данни. - Осигурете сигурността на вашите предварителни заявки: Уверете се, че вашият сървър обработва правилно предварителните заявки и връща правилните CORS хедъри.
- Използвайте HTTPS: Винаги използвайте HTTPS както за източника, така и за ресурсите, до които осъществявате достъп от друг източник. Това помага за защита срещу атаки от типа „човек по средата“ (man-in-the-middle).
- 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, но му пречи да зарежда скриптове или да се свързва с други домейни.
Заключение
Политиката за сигурност на съдържанието (CSP) и Споделянето на ресурси между източници (CORS) са основни инструменти за укрепване на сигурността на вашите frontend приложения. Чрез внимателно конфигуриране на CSP и CORS можете значително да намалите риска от XSS атаки, атаки с инжектиране на данни и други уязвимости в сигурността. Не забравяйте да започнете с рестриктивна политика, да тествате обстойно и непрекъснато да наблюдавате и усъвършенствате вашата конфигурация, за да се адаптирате към промените във вашето приложение и развиващата се среда на заплахи. Като приоритизирате сигурността на frontend, можете да защитите потребителите си и да гарантирате целостта на вашите уеб приложения в днешния все по-сложен дигитален свят.