Kompleksowy przewodnik po wzmacnianiu bezpieczeństwa frontendu za pomocą Content Security Policy (CSP) i Cross-Origin Resource Sharing (CORS), chroniący aplikacje internetowe przed nowoczesnymi zagrożeniami.
Wzmacnianie bezpieczeństwa frontendu: Content Security Policy i CORS
W dzisiejszym połączonym cyfrowym świecie bezpieczeństwo frontendu jest sprawą najwyższej wagi. Aplikacje internetowe są coraz częściej celem zaawansowanych ataków, co sprawia, że solidne środki bezpieczeństwa są niezbędne. Dwa kluczowe komponenty bezpiecznej architektury frontendu to Content Security Policy (CSP) i Cross-Origin Resource Sharing (CORS). Ten kompleksowy przewodnik dogłębnie omawia te technologie, oferując praktyczne przykłady i użyteczne wskazówki, które pomogą Ci wzmocnić swoje aplikacje internetowe przed nowoczesnymi zagrożeniami.
Czym jest Content Security Policy (CSP)?
Content Security Policy (CSP) to dodatkowa warstwa zabezpieczeń, która pomaga wykrywać i łagodzić niektóre rodzaje ataków, w tym Cross-Site Scripting (XSS) i ataki typu data injection. CSP jest implementowane poprzez wysłanie przez serwer WWW nagłówka odpowiedzi HTTP Content-Security-Policy do przeglądarki. Ten nagłówek definiuje białą listę źródeł, z których przeglądarka może ładować zasoby. Ograniczając źródła treści, które przeglądarka może załadować, CSP znacznie utrudnia atakującym wstrzyknięcie złośliwego kodu na Twoją stronę internetową.
Jak działa CSP
CSP działa, instruując przeglądarkę, aby ładowała zasoby (np. skrypty, arkusze stylów, obrazy, czcionki) tylko z zatwierdzonych źródeł. Te źródła są określone w nagłówku CSP za pomocą dyrektyw. Jeśli przeglądarka spróbuje załadować zasób ze źródła, które nie jest jawnie dozwolone, zablokuje żądanie i zgłosi naruszenie.
Dyrektywy CSP: Kompleksowy przegląd
Dyrektywy CSP kontrolują typy zasobów, które mogą być ładowane z określonych źródeł. Oto omówienie niektórych z najważniejszych dyrektyw:
- default-src: Określa domyślne źródło dla wszystkich typów treści. Jest to dyrektywa zapasowa, która ma zastosowanie, gdy brakuje innych, bardziej szczegółowych dyrektyw.
- script-src: Określa źródła, z których mogą być ładowane skrypty. Jest to kluczowe dla zapobiegania atakom XSS.
- style-src: Określa źródła, z których mogą być ładowane arkusze stylów.
- img-src: Określa źródła, z których mogą być ładowane obrazy.
- font-src: Określa źródła, z których mogą być ładowane czcionki.
- media-src: Określa źródła, z których mogą być ładowane pliki audio i wideo.
- object-src: Określa źródła, z których mogą być ładowane wtyczki (np. Flash). Często ustawia się na 'none', aby całkowicie wyłączyć wtyczki ze względu na ich wrodzone ryzyka bezpieczeństwa.
- frame-src: Określa źródła, z których mogą być ładowane ramki (np. <iframe>).
- connect-src: Określa adresy URL, z którymi agent użytkownika może się łączyć za pomocą interfejsów skryptowych, takich jak XMLHttpRequest, WebSocket i EventSource.
- base-uri: Określa adresy URL, które mogą być używane w elemencie <base> dokumentu.
- form-action: Określa adresy URL, do których mogą być wysyłane dane z formularzy.
- upgrade-insecure-requests: Instruuje agenta użytkownika, aby automatycznie uaktualniał niezabezpieczone żądania (HTTP) do bezpiecznych żądań (HTTPS).
- report-uri: Określa adres URL, na który przeglądarka powinna wysyłać raporty o naruszeniach CSP. Ta dyrektywa jest przestarzała na rzecz `report-to`.
- report-to: Określa nazwę grupy raportowania zdefiniowaną w nagłówku `Report-To`, do której przeglądarka powinna wysyłać raporty o naruszeniach CSP.
Słowa kluczowe listy źródeł CSP
W dyrektywach CSP można używać słów kluczowych listy źródeł do definiowania dozwolonych źródeł. Oto niektóre popularne słowa kluczowe:
- 'self': Pozwala na zasoby z tego samego źródła (schemat i host), co dokument.
- 'none': Nie zezwala na zasoby z żadnych źródeł.
- 'unsafe-inline': Pozwala na użycie wbudowanych skryptów i stylów (np. tagów <script> i atrybutów style). Należy używać z najwyższą ostrożnością, ponieważ znacznie osłabia to ochronę CSP przed XSS.
- 'unsafe-eval': Pozwala na użycie funkcji dynamicznej ewaluacji kodu, takich jak
eval()iFunction(). Należy używać z najwyższą ostrożnością, ponieważ wprowadza to znaczne ryzyka bezpieczeństwa. - 'unsafe-hashes': Pozwala na określone wbudowane procedury obsługi zdarzeń lub tagi <style>, które pasują do określonego hasha. Wymaga wsparcia przeglądarki. Należy używać ostrożnie.
- 'strict-dynamic': Określa, że zaufanie jawnie udzielone skryptowi obecnemu w kodzie strony, poprzez dołączenie do niego wartości nonce lub hasha, zostanie przeniesione na wszystkie skrypty ładowane przez ten skrypt główny.
- data: Pozwala na identyfikatory URI typu data: (np. wbudowane obrazy zakodowane jako base64). Należy używać ostrożnie.
- https:: Pozwala na ładowanie zasobów przez HTTPS z dowolnej domeny.
- [hostname]: Pozwala na zasoby z określonej domeny (np. example.com). Można również określić numer portu (np. example.com:8080).
- [scheme]://[hostname]:[port]: W pełni kwalifikowany identyfikator URI, pozwalający na zasoby z określonego schematu, hosta i portu.
Praktyczne przykłady CSP
Przyjrzyjmy się kilku praktycznym przykładom nagłówków CSP:
Przykład 1: Podstawowe CSP z 'self'
Ta polityka zezwala na zasoby tylko z tego samego źródła:
Content-Security-Policy: default-src 'self'
Przykład 2: Zezwalanie na skrypty z określonej domeny
Ta polityka zezwala na skrypty z Twojej własnej domeny oraz z zaufanej sieci CDN:
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com
Przykład 3: Wyłączanie wbudowanych skryptów i stylów
Ta polityka nie zezwala na wbudowane skrypty i style, co stanowi silną obronę przed XSS:
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'
Ważne: Wyłączenie wbudowanych skryptów wymaga refaktoryzacji kodu HTML w celu przeniesienia ich do zewnętrznych plików.
Przykład 4: Używanie nonce dla wbudowanych skryptów
Jeśli musisz używać wbudowanych skryptów, użyj wartości nonce (kryptograficznie losowych, jednorazowych tokenów), aby umieścić na białej liście określone bloki skryptów. Jest to bezpieczniejsze niż 'unsafe-inline'. Serwer musi wygenerować unikalny nonce dla każdego żądania i umieścić go zarówno w nagłówku CSP, jak i w tagu <script>.
Content-Security-Policy: default-src 'self'; script-src 'nonce-r4nd0mN0nc3'; style-src 'self'
<script nonce="r4nd0mN0nc3"> console.log('Inline script'); </script>
Uwaga: Pamiętaj, aby generować nowy nonce dla każdego żądania. Nie używaj ponownie tych samych wartości nonce!
Przykład 5: Używanie hashy dla wbudowanych stylów
Podobnie jak w przypadku nonce, hashe mogą być używane do umieszczania na białej liście określonych wbudowanych bloków <style>. Odbywa się to poprzez wygenerowanie hasha SHA256, SHA384 lub SHA512 z treści stylu.
Content-Security-Policy: default-src 'self'; style-src 'sha256-HASHEDSTYLES'
<style sha256="HASHEDSTYLES"> body { background-color: #f0f0f0; } </style>
Uwaga: Hashe są mniej elastyczne niż nonce, ponieważ każda zmiana w treści stylu unieważni hash.
Przykład 6: Raportowanie naruszeń CSP
Aby monitorować naruszenia CSP, użyj dyrektywy report-uri lub report-to:
Content-Security-Policy: default-src 'self'; report-to csp-endpoint;
Będziesz także musiał skonfigurować nagłówek Report-To. Nagłówek Report-To definiuje jedną lub więcej grup raportowania, które określają, gdzie i jak raporty powinny być wysyłane.
Report-To: {"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"https://example.com/csp-report"}]}
Testowanie i wdrażanie CSP
Wdrożenie CSP wymaga starannego planowania i testowania. Zacznij od restrykcyjnej polityki i stopniowo ją rozluźniaj w miarę potrzeb. Użyj nagłówka Content-Security-Policy-Report-Only, aby przetestować swoją politykę bez blokowania zasobów. Ten nagłówek raportuje naruszenia bez egzekwowania polityki, co pozwala zidentyfikować i naprawić problemy przed wdrożeniem polityki w środowisku produkcyjnym.
Content-Security-Policy-Report-Only: default-src 'self'; report-to csp-endpoint;
Analizuj raporty generowane przez przeglądarkę, aby zidentyfikować wszelkie naruszenia i odpowiednio dostosować swoją politykę. Gdy będziesz pewien, że Twoja polityka działa poprawnie, wdróż ją za pomocą nagłówka Content-Security-Policy.
Dobre praktyki dla CSP
- Zacznij od default-src: Zawsze definiuj
default-src, aby ustalić podstawową politykę. - Bądź konkretny: Używaj szczegółowych dyrektyw i słów kluczowych listy źródeł, aby ograniczyć zakres swojej polityki.
- Unikaj 'unsafe-inline' i 'unsafe-eval': Te słowa kluczowe znacznie osłabiają CSP i powinny być unikane, gdy tylko to możliwe.
- Używaj nonce lub hashy dla wbudowanych skryptów i stylów: Jeśli musisz używać wbudowanych skryptów lub stylów, używaj nonce lub hashy, aby umieścić na białej liście określone bloki kodu.
- Monitoruj naruszenia CSP: Używaj dyrektywy
report-urilubreport-to, aby monitorować naruszenia CSP i odpowiednio dostosowywać swoją politykę. - Testuj dokładnie: Używaj nagłówka
Content-Security-Policy-Report-Only, aby przetestować swoją politykę przed wdrożeniem jej w środowisku produkcyjnym. - Iteruj i udoskonalaj: CSP to nie jednorazowa konfiguracja. Ciągle monitoruj i udoskonalaj swoją politykę, aby dostosować ją do zmian w aplikacji i krajobrazie zagrożeń.
Czym jest Cross-Origin Resource Sharing (CORS)?
Cross-Origin Resource Sharing (CORS) to mechanizm, który pozwala stronom internetowym z jednego źródła (domeny) na dostęp do zasobów z innego źródła. Domyślnie przeglądarki egzekwują politykę tego samego pochodzenia (Same-Origin Policy), która uniemożliwia skryptom wysyłanie żądań do innego źródła niż to, z którego pochodzi skrypt. CORS zapewnia sposób na selektywne złagodzenie tego ograniczenia, umożliwiając legalne żądania międzydomenowe, jednocześnie chroniąc przed złośliwymi atakami.
Zrozumienie Same-Origin Policy
Same-Origin Policy to fundamentalny mechanizm bezpieczeństwa, który uniemożliwia złośliwemu skryptowi z jednej strony internetowej dostęp do wrażliwych danych na innej stronie. Źródło jest definiowane przez schemat (protokół), hosta (domenę) i port. Dwa adresy URL mają to samo źródło wtedy i tylko wtedy, gdy mają ten sam schemat, hosta i port.
Na przykład:
https://www.example.com/app1/index.htmlihttps://www.example.com/app2/index.htmlmają to samo źródło.https://www.example.com/index.htmlihttp://www.example.com/index.htmlmają różne źródła (inny schemat).https://www.example.com/index.htmlihttps://sub.example.com/index.htmlmają różne źródła (inny host).https://www.example.com:8080/index.htmlihttps://www.example.com:80/index.htmlmają różne źródła (inny port).
Jak działa CORS
Gdy strona internetowa wysyła żądanie międzydomenowe, przeglądarka najpierw wysyła żądanie „preflight” do serwera. Żądanie preflight używa metody HTTP OPTIONS i zawiera nagłówki wskazujące metodę HTTP oraz nagłówki, których użyje właściwe żądanie. Serwer następnie odpowiada nagłówkami, które wskazują, czy żądanie międzydomenowe jest dozwolone.
Jeśli serwer zezwala na żądanie, zawiera w odpowiedzi nagłówek Access-Control-Allow-Origin. Ten nagłówek określa źródło (lub źródła), które mają zezwolenie na dostęp do zasobu. Przeglądarka następnie kontynuuje z właściwym żądaniem. Jeśli serwer nie zezwala na żądanie, nie zawiera nagłówka Access-Control-Allow-Origin, a przeglądarka blokuje żądanie.
Nagłówki CORS: Szczegółowe omówienie
CORS opiera się na nagłówkach HTTP do komunikacji między przeglądarką a serwerem. Oto kluczowe nagłówki CORS:
- Access-Control-Allow-Origin: Określa źródło (lub źródła), które mają zezwolenie na dostęp do zasobu. Ten nagłówek może zawierać określone źródło (np.
https://www.example.com), symbol wieloznaczny (*) lubnull. Użycie*pozwala na żądania z dowolnego źródła, co generalnie nie jest zalecane ze względów bezpieczeństwa. Użycie `null` jest odpowiednie tylko dla „nieprzezroczystych odpowiedzi”, na przykład gdy zasób jest pobierany za pomocą protokołu `file://` lub data URI. - Access-Control-Allow-Methods: Określa metody HTTP, które są dozwolone dla żądania międzydomenowego (np.
GET, POST, PUT, DELETE). - Access-Control-Allow-Headers: Określa nagłówki HTTP, które są dozwolone w żądaniu międzydomenowym. Jest to ważne przy obsłudze niestandardowych nagłówków.
- Access-Control-Allow-Credentials: Wskazuje, czy przeglądarka powinna dołączać poświadczenia (np. pliki cookie, nagłówki autoryzacyjne) do żądania międzydomenowego. Ten nagłówek musi być ustawiony na
true, aby zezwolić na poświadczenia. - Access-Control-Expose-Headers: Określa, które nagłówki mogą być udostępnione klientowi. Domyślnie udostępniany jest tylko ograniczony zestaw nagłówków.
- Access-Control-Max-Age: Określa maksymalny czas (w sekundach), przez który przeglądarka może buforować żądanie preflight.
- Origin: To jest nagłówek żądania wysyłany przez przeglądarkę w celu wskazania źródła żądania.
- Vary: Ogólny nagłówek HTTP, ale ważny dla CORS. Gdy
Access-Control-Allow-Originjest generowany dynamicznie, nagłówekVary: Originpowinien być zawarty w odpowiedzi, aby poinstruować mechanizmy buforowania, że odpowiedź różni się w zależności od nagłówka żądaniaOrigin.
Praktyczne przykłady CORS
Przyjrzyjmy się kilku praktycznym przykładom konfiguracji CORS:
Przykład 1: Zezwalanie na żądania z określonego źródła
Ta konfiguracja zezwala na żądania tylko z https://www.example.com:
Access-Control-Allow-Origin: https://www.example.com
Przykład 2: Zezwalanie na żądania z dowolnego źródła (niezalecane)
Ta konfiguracja zezwala na żądania z dowolnego źródła. Należy używać ostrożnie, ponieważ może to wprowadzić ryzyka bezpieczeństwa:
Access-Control-Allow-Origin: *
Przykład 3: Zezwalanie na określone metody i nagłówki
Ta konfiguracja zezwala na metody GET, POST i PUT oraz nagłówki Content-Type i Authorization:
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type, Authorization
Przykład 4: Zezwalanie na poświadczenia
Aby zezwolić na poświadczenia (np. pliki cookie), musisz ustawić Access-Control-Allow-Credentials на true i określić konkretne źródło (nie można używać * przy zezwalaniu na poświadczenia):
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Credentials: true
Musisz także ustawić credentials: 'include' w swoim żądaniu JavaScript fetch/XMLHttpRequest.
fetch('https://api.example.com/data', {
credentials: 'include'
})
Zapytania preflight w CORS
Dla niektórych typów żądań międzydomenowych (np. żądań z niestandardowymi nagłówkami lub metodami innymi niż GET, HEAD lub POST z Content-Type o wartości application/x-www-form-urlencoded, multipart/form-data lub text/plain), przeglądarka wysyła żądanie preflight używając metody OPTIONS. Serwer musi odpowiedzieć na żądanie preflight odpowiednimi nagłówkami CORS, aby wskazać, czy właściwe żądanie jest dozwolone.
Oto przykład żądania i odpowiedzi preflight:
Żądanie preflight (OPTIONS):
OPTIONS /data HTTP/1.1
Origin: https://www.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type, Authorization
Odpowiedź preflight (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
Nagłówek Access-Control-Max-Age określa, jak długo przeglądarka może buforować odpowiedź preflight, zmniejszając liczbę żądań preflight.
CORS a JSONP
JSON with Padding (JSONP) to starsza technika omijania Same-Origin Policy. Jednak JSONP wiąże się ze znacznymi ryzykami bezpieczeństwa i należy go unikać na rzecz CORS. JSONP polega na wstrzykiwaniu tagów <script> na stronę, co może prowadzić do wykonania dowolnego kodu. CORS zapewnia bezpieczniejszy i bardziej elastyczny sposób obsługi żądań międzydomenowych.
Dobre praktyki dla CORS
- Unikaj używania *: Unikaj używania symbolu wieloznacznego (*) w nagłówku
Access-Control-Allow-Origin, ponieważ pozwala to na żądania z dowolnego źródła. Zamiast tego określ konkretne źródło (lub źródła), które mają zezwolenie na dostęp do zasobu. - Bądź konkretny co do metod i nagłówków: Określ dokładne metody HTTP i nagłówki, które są dozwolone w nagłówkach
Access-Control-Allow-MethodsiAccess-Control-Allow-Headers. - Używaj Access-Control-Allow-Credentials z ostrożnością: Włączaj
Access-Control-Allow-Credentialstylko wtedy, gdy musisz zezwolić na poświadczenia (np. pliki cookie) w żądaniach międzydomenowych. Bądź świadomy implikacji bezpieczeństwa związanych z zezwalaniem na poświadczenia. - Zabezpiecz swoje żądania preflight: Upewnij się, że Twój serwer prawidłowo obsługuje żądania preflight i zwraca poprawne nagłówki CORS.
- Używaj HTTPS: Zawsze używaj HTTPS zarówno dla źródła, jak i zasobów, do których uzyskujesz dostęp międzydomenowo. Pomaga to chronić przed atakami typu man-in-the-middle.
- Vary: Origin: Jeśli dynamicznie generujesz nagłówek `Access-Control-Allow-Origin`, zawsze dołączaj nagłówek `Vary: Origin`, aby zapobiec problemom z buforowaniem.
CSP i CORS w praktyce: Podejście połączone
Chociaż zarówno CSP, jak i CORS dotyczą kwestii bezpieczeństwa, działają na różnych warstwach i zapewniają komplementarną ochronę. CSP koncentruje się na zapobieganiu ładowaniu przez przeglądarkę złośliwej zawartości, podczas gdy CORS koncentruje się na kontrolowaniu, które źródła mogą uzyskiwać dostęp do zasobów na Twoim serwerze.
Łącząc CSP i CORS, możesz stworzyć bardziej solidną postawę bezpieczeństwa dla swoich aplikacji internetowych. Na przykład, możesz użyć CSP do ograniczenia źródeł, z których mogą być ładowane skrypty, a CORS do kontrolowania, które źródła mogą uzyskiwać dostęp do Twoich punktów końcowych API.
Przykład: Zabezpieczanie API za pomocą CSP i CORS
Załóżmy, że masz API hostowane pod adresem https://api.example.com, które ma być dostępne tylko z https://www.example.com. Możesz skonfigurować swój serwer tak, aby zwracał następujące nagłówki:
Nagłówki odpowiedzi API (https://api.example.com):
Access-Control-Allow-Origin: https://www.example.com
Content-Type: application/json
I możesz skonfigurować swoją stronę internetową (https://www.example.com) tak, aby używała następującego nagłówka CSP:
Nagłówek CSP strony internetowej (https://www.example.com):
Content-Security-Policy: default-src 'self'; script-src 'self'; connect-src 'self' https://api.example.com;
Ta polityka CSP pozwala stronie internetowej na ładowanie skryptów i łączenie się z API, ale uniemożliwia jej ładowanie skryptów lub łączenie się z innymi domenami.
Podsumowanie
Content Security Policy (CSP) i Cross-Origin Resource Sharing (CORS) to niezbędne narzędzia do wzmacniania bezpieczeństwa Twoich aplikacji front-endowych. Poprzez staranną konfigurację CSP i CORS, możesz znacznie zmniejszyć ryzyko ataków XSS, ataków typu data injection i innych luk w zabezpieczeniach. Pamiętaj, aby zacząć od restrykcyjnej polityki, dokładnie testować i stale monitorować oraz udoskonalać swoją konfigurację, aby dostosować ją do zmian w aplikacji i ewoluującego krajobrazu zagrożeń. Priorytetowo traktując bezpieczeństwo frontendu, możesz chronić swoich użytkowników i zapewnić integralność swoich aplikacji internetowych w dzisiejszym coraz bardziej złożonym cyfrowym świecie.