Preorder drugiego tomu książki sekuraka: Wprowadzenie do bezpieczeństwa IT. -15% z kodem: sekurak-book
Czym jest Content Security Policy?
Wstęp
W dobie coraz powszechniejszych zagrożeń, czyhających na nas w cyberprzestrzeni, coraz bardziej istotną kwestią staje się poziom zabezpieczeń, jaki oferują nam ich twórcy na każdym etapie korzystania z aplikacji webowych. Rosnąca świadomość zagrożeń zmusza organizacje, zajmujące się tworzeniem i rozwijaniem standardów dla internetu, do wdrażania mechanizmów pozwalających na skuteczniejszą walkę z próbami ataków na aplikacje webowe.
Jednym z takich standardów jest Content Security Policy (CSP). Jej autorem są Adam Barth i Mike West z Google Inc. oraz Dan Veditz z Mozilla Foundation. Całość jest opracowywana pod skrzydłami W3C w ramach grupy roboczej zorientowanej wokół bezpieczeństwa. W chwili pisania tego artykułu najnowszą wersją CSP 1.1 był szkic roboczy z 4. lipca 2013 roku (dostępny pod adresem Content Security Policy 1.1, W3C Working Draft 04 June 2013).
Czym jest CSP? W największym skrócie – Content Security Policy pozwala twórcy aplikacji webowej na ścisłe zdefiniowanie, skąd mogą pochodzić dodatkowe zasoby, z których korzysta aplikacja www (pliki zewnętrzne JavaScript czy CSS, obrazki i inne elementy multimedialne). Ograniczenie to ma zapobiegać atakom XSS, które dołączają do źródeł strony skrypty doczytywane z innych lokalizacji sieciowych.
Mówiąc inaczej, CSP umożliwia utworzenie 'whitelist’ (listy dopuszczalnych wartości) tych zasobów zewnętrznych (bądź skryptów), które mogą być w aplikacji użyte. Wszystkie nie pasujące do polityki zasoby będą przez przeglądarkę odrzucone jako niedozwolone, a sama przeglądarka zgłosi błąd naruszenia reguł polityki.
1. Przykładowy komunikat błędu zgłoszony przez przeglądarkę Chrome w przypadku naruszenia Content Security Policy (źródło: http://www.html5rocks.com/en/tutorials/security/content-security-policy/csp-error.png)
Content Security Policy w akcji
CSP 1.1 zawiera definicję dwóch nagłówków odpowiedzi HTTP, które serwer może ustawić. Są to:
Content-Security-Policy
Content-Security-Policy-Report-Only
W fazie eksperymentalnej znajdują się odpowiedniki tych nagłówków, które będzie można umieszczać w sekcji <head> dokumentu HTML, w postaci znaczników <meta> (http-equiv=”content-security-policy” i http-equiv=”content-security-policy-report-only”).W treści nagłówka definiujemy Security Policy dla naszej aplikacji. Do dyspozycji mamy tzw. dyrektywy, za pomocą których możemy bardzo szczegółowo określić, co przeglądarka może załadować i uruchomić w kontekście wykonywanego kodu.
script-src to dyrektywa odpowiedzialna za definiowanie reguł dla JavaScript. Może ona przyjąć postać ciągu zawierającego wskazanie konkretnych URI, z których skrypty mogą zostać uruchomione (jeśli dany URI nie znajdzie się na tej liście, dołączenie go znacznikiem <script src=”http://URI/script.js”> nie spowoduje jego załadowania, a przeglądarka zgłosi błąd naruszenia polityki CSP).
Ciąg może zawierać dowolną ilość adresów. Mogą to być adresy jednoznacznie identyfikujące daną domenę, ale możemy używać także tzw. 'wildcard’, czyli znaku '*’ określającego dowolną wartość w miejscu, gdzie został on zastosowany. Może to być: protokół, nazwa domenowa, subdomena czy port.
Ostatnią możliwą wartością jest ’self’ (ujęte w apostrofy – to bardzo ważne, by o tym pamiętać, w innym przypadku słowo self zostanie potraktowane jako… host o takiej nazwie). Słowo to wskazuje na możliwość wykonywania wszsytkich skryptów JavaScript pochodzących z dokładnie tej samej domeny, z której pochodzi wywołujący dokument.
Pora na przykład praktyczny. Wyobraźmy sobie sytuację, że nasz serwis wszystkie zewnętrzne pliki z kodem JavaScript zaczytuje z serwera CDN (Content Delivery Network) zlokalizowanego pod adresem http://cdn.greatstuff.serv. Dodatkowo kilka prostych skryptów znajduje się bezpośrednio na naszym serwerze. W takiej sytuacji prawidłowo skontruowany nagłówek CSP powinien mieć taką postać:
Content-Security-Policy: script-src 'self' http://cdn.greatstuff.serv
Oznacza to, że w przypadku udanego ataku Persistent/Stored XSS oraz osadzenia przez atakującego w payloadzie XSS znacznika <script> zawierającego atrybut 'src’ wskazujący na skrypt w domenie http://agresor.evil.com – kod nie załaduje i nie wykona się, gdyż adres nie jest dopuszczony przez CSP.
default-src jest brana pod uwagę w momencie, gdy nie jest zdefiniowana żadna dyrektywa dla konkretnego typu zasobów (przykładowo – gdy nie ma definicji script-src, ale jest default-src – to ona będzie brana pod uwagę w przypadku rozpatrywania CSP dla plików JavaScript). Wartość zdefiniowana w default-src nie podlega dziedziczeniu, to znaczy, że jawne zdefiniowanie dyrektywy dla innego typu zasobów nadpisuje reguły z default-src (ale tylko i wyłącznie dla tego konkretnego typu, dla pozostałych, nie zdefiniowanych jawnie, nadal ma zastosowanie reguła z default-src).
connect-src odpowiada za wszystkie połączenia, realizowane przez przeglądarkę poprzez:
- Web Sockets
- metodę
open()
obiektu XMLHttpRequest - obsługę odbierania informacji o zdarzeniach od serwera (server sent events) przez mechanizm EventSource
font-src pozwala na określenie CSP dla fontów ładowanych poprzez reguły @font-face kaskadowych arkuszy stylów CSS
frame-src, bardzo ważna w kontekście ataków XSS, w których agresor próbuje osadzić element <iframe> (ramkę) w źródle strony i poprzez jej atrybut 'src’ załadować i wykonać złośliwy kod. frame-src pozwala nam na ograniczenie potencjalnych adresów, z których mogą być załadowane pliki JavaScript w taki sam sposób, jak dyrektywa script-src.
img-src definiuje reguły CSP dla obrazków. Reguły te zadziałają dla:
- atrybutu 'src’ znacznika <img>
- wartości url(…) lub image(…) w kaskadowych arkuszach styli CSS
- wartości atrybutu href znacznika <link rel>, jeśli odnosi się on do obrazka (np. ikony)
media-src odpowiada za atrybut src elementów <video>, <audio>, <source> oraz <track>.
object-src – jak wyżej, ale dla elementów <object> i <embed>
style-src – dla zewnętrznych arkuszy styli CSS
Ostatnią dyrektywą jest report-uri, nie będąca bezpośrednio odpowiedzialna za konkretną politykę. report-uri służy do zdefiniowania adresu URL, pod który przeglądarka ma wysyłać raporty o naruszeniu Content Security Policy. To w naturalny sposób wprowadza nas w zagadnienia związane z drugim nagłówkiem CSP 1.1, o którym jeszcze szczegółowo nie mówiliśmy, czyli…
Content-Security-Policy-Report-Only, czyli kontrola bez reakcji
Poza trybem działania wymuszonym przez definicje dyrektyw w nagłówku Content-Security-Policy, CSP może działać w tzw. trybie raportowania. Wszystkie reguły definiuje się dokładnie w ten sam sposób, jednakże przeglądarka nie będzie blokowała zasobów, a jedynie raportowała zaistniałe naruszenie CSP.
Raportowanie odbywa się poprzez wysyłanie przez przeglądarkę żądania metodą POST protokołu HTTP (na adres podany w dyrektywie report-uri) raportu w formacie JSON zawierającego wszystkie informacje konieczne do identyfikacji incydentu naruszenia CSP. I tak, w raporcie możemy znaleźć tzw. referrer, zablokowany zasób czy wskazanie, która reguła została przez niego naruszona:
{ "csp-report": { "document-uri": "http://example.org/page.html", "referrer": "http://evil.example.com/", "blocked-uri": "http://evil.example.com/evil.js", "violated-directive": "script-src 'self' https://apis.google.com", "original-policy": "script-src 'self' https://apis.google.com; report-uri http://example.org/my_amazing_csp_report_parser" } }
2. Przykładowy raport naruszenia Content Security Policy (źródło: http://www.html5rocks.com/en/tutorials/security/content-security-policy/#reporting)
Wykorzystanie nagłówka Content-Security-Policy-Report-Only jest doskonałym sposobem na przetestowanie, czy nasza polityka została zdefiniowana prawidłowo, to znaczy, czy blokuje wszystkie zasoby nie autoryzowane, a dopuszcza te, które powinny zostać użyte, nie blokując jednocześnie działania samej aplikacji.
Gdy przestaniemy już otrzymywać powiadomienia o błędach, a nasz serwis czy aplikacja będzie działać prawidłowo, wtedy możemy zamienić nagłówek Content-Security-Policy-Report-Only na aktywnie blokujący Content-Security-Policy i cieszyć się zwiększonym bezpieczeństwem kodu.
Walka z inline
Do tej pory skupialiśmy się na zablokowaniu zasobów pochodzących ze źródeł zewnętrznych, gdy tymczasem payloady XSS to z reguły fragmenty kodu osadzone bezpośrednio w treści strony www. Otóż sytuacje te, chyba, że jawnie na nie zezwolimy przy użyciu opisanych poniżej dyrektyw, nie będą miały miejsca – osadzony kod w przypadku użycia Content Security Policy po prostu się nie wykona.
Twórcy specyfikacji Content Security Policy oczywiście wzięli pod uwagę, że ktoś może świadomie chcieć dopuścić inne zachowanie. Do dyspozycji dali programistom dwie dyrektywy: unsafe-inline oraz unsafe-eval. Można ich użyć ZAMIAST 'self’ dopuszczając tym samym konstrukcje, które są wyłączone domyślnie. Przyjrzyjmy im się dokładniej.
Użycie unsafe-inline zezwala przeglądarce na wykonanie kodu JavaScript, znajdującego się:
- jako fragment strony, pomiędzy znacznikami <script> oraz </script>
- jako kod zdefiniowany w postaci atrybutu znacznika HTML on[event], np. onclick=”jakasfunckja()”. Także taki kod zadziała prawidłowo, gdy użyjemy unsafe-inline
W tym miejscu warto zwrócić uwagę na pewną kwestię techniczną, mianowicie używanie Content Security Policy wymusza taką organizację kodu, by cały JavaScript znajdował się w zewnętrznym (bądź zewnętrznych) plikach, a obsługa zdarzeń, do tej pory realizowana na zasadzie wykorzystywania takich inline’owych konstrukcji, została przeniesiona na zewnątrz (element.addEventListener()). Związane jest to m.in. z koncepcją odseparowania od siebie warstw prezentacji (HTML i CSS) od logiki (JavaScript). Dokładniejsze omówienie tego zagadnienia wykracza poza ramy tego artykułu i jest raczej domeną programistów języka JavaScript, a nie osób odpowiedzialnych za bezpieczeństwo – niemniej jednak należy o tym pamiętać.unsafe-eval to druga dyrektywa związana z kodem JS na stronie www. Dopuszcza ona możliwość wykonywania następujących wbudowanych funkcji języka JavaScript:
- eval(), która wykonuje podany jej jako argument ciąg znaków jakby był to kod JavaScript:
eval("alert('XSS!!!')");
jest równoznaczne z
alert('XSS!');
- setTimeout() oraz setInterval(), które jako swój pierwszy argument także mogą przyjąć ciąg znaków i zinterpretują go jako kod JS
- konstruktor Function(), gdzie argumentem jest ciąg znaków definiujący i zwracający nową funkcję: The Function constructor
Zalecane jest nie korzystanie z dyrektyw unsafe-inline oraz unsafe-eval.
Na koniec przykładowa dyrektywa script-src, która zawiera reguły umożliwiające umieszczenie na swojej stronie przycisków „Tweet”, „G+” oraz „Like”:
script-src https://apis.google.com https://platform.twitter.com; frame-src https://plusone.google.com https://facebook.com https://platform.twitter.com
Inne nagłówki i technologie HTTP, które zwiększają bezpieczeństwo aplikacji webowych
Oczywiście Content-Security-Policy oraz Content-Security-Policy-Report-Only nie wyczerpują szerokiego wachlarza możliości, jakim dysponuje dzisiaj twórca aplikacji webowej. Do arsenału środków obronnych można, a nawet należy, dołączyć:
- Cross-Origin Resource Sharing – CORS,W3C Recommendation 16 January 2014
- X-Frame-Options – The X-Frame-Options response header, a dla dociekliwych RFC 7034
- X-Content-Type-Options – IE8 Security Part VI: Beta 2 Update
- HTTP Strict Transport Security – HTTP Strict Transport Security
- X-XSS-Protection – IE8 Security Part IV: The XSS Filter
Warto choćby pobieżnie zapoznać się z przedstawionymi powyżej technikami. Ich konsekwentne i odpowiednie stosowanie może drastycznie podnieść poziom bezpieczeństwa naszej aplikacji bądź serwisu internetowego, w szczególności na ataki XSS, CSRF oraz pokrewne.
Źródła
- An Introduction to Content Security Policy | HTML5 Rocks
- CSP to the Rescue: Leveraging the Browser for Security | Twitter Engineering Blog
- 4 HTTP SECURITY HEADERS YOU SHOULD ALWAYS BE USING | iBuildings Blog
- Content Security Policy 1.1; W3C Working Draft 04 June 2013 | W3C
- Cross-Origin Resource Sharing; W3C Recommendation 16 January 2014 | W3C
Rafał 'bl4de’ Janicki– bloorq[at]gmail.com
Bardzo fajny temat, coraz więcej Twoich tekstów widzę, bl4de, dołączasz do ekipy? :D
Szczerze powiedziawszy muszę Ci powiedzieć, że też miałem chrapkę, aby opisać ten temat, ale fajnie że taki tekst pojawił się na Sekuraku. CSP to potężne narzędzie i odpowiednio użyte potrafi naprawdę poważnie zagotować krew podczas eksploitacji XSS. Szkoda, że trochę kulawe jest jego wsparcie i że trochę taki 'burdel’ jest w nazwach nagłówków.
W artykule wspominałeś o CSP 1.1. Warto wspomnieć o fajnej rzeczy, którą przemycono do tej nowej wersji standardu – chodzi mi o CSP 1.1 script nonce. Jest to furtka dla wszystkich skomplikowanych aplikacji RIA albo potworków takich jak ASP WebForms, które dynamicznie dodaje inline javascripts. Jak ktoś oswoi się już z podstawami CSP opisanymi przez bl4d3, polecam przyjrzeć się również ficzerom z CSP 1.1 :)
@vizzdoom
Dzięki :)
CSP 1.1 to rzecz stosunkowo mało znana i dość rzadko używana, poza wspomnianymi przez Ciebie nounce, jest jeszcze kilka takich smaczków – ale to już polecam uważną lekturę draftu specyfikacji na stronie W3C :)
Dobre. Rozsyłam znajomym.
Hmmm… a jest tu jakiś programista PHP/JS z dobrą obsługą serwerów do współpracy? ;) Bo pisać teksty to ludzie są, a jak przychodzi coś praktycznego, to ludziów nie ma.
józek – tacy ludzie po prostu już pracują ;) ale to w zasadzie niezły offtopic
@vizzdoom
smutne ale prawdziwe ;) na szczęście w końcu wziąłem się do roboty i ostro ogarniam Node.JS z PHPem, jeszcze z miesiąc i będę samowystarczalny. Taaaakieee pieniąąądzeeee!
@józek
Powodzenia, dobrze idziesz :)
Porządnych artykuł i przydatny, dzięki za niego ;)
Rozumiem że wszystko dzieje się po stronie przeglądarki? Wszystkie wspierają taką ochronę?
@ziggurad
Tak, dokładnie, to przeglądarka odpowiada za interpretowanie nagłówków CSP.
Ze wsparciem, jak to generalnie w technologiach HTML5 bywa, jest różnie w zależności od konkretnej przeglądarki i wersji, ale wygląda całkiem ok na dzień dzisiejszy. Aktualnie Content Security Policy wspierają:
– Firefox od wersji 26 (pełne wsparcie)
– Chrome od wersji 31 (pełne wsparcie)
– Safari od wersji 6.0 (pełne wsparcie)
– Opera od wersji 19 (pełne wsparcie)
– Safari dla iOS od wersji 6.0 (pełne wsparcie)
– Android Browser od 4.4 (pełne wsparcie)
– Blackberry Browser od wersji 10 (pełne wsparcie)
Czyli generalnie WebKit, Blink oraz Gecko – co nie jest niespodzianką biorąc pod uwagę fakt, że w opracowywanie CSP zaangażowane są, poza W3C, Google i Mozilla
– Internet Explorer – częściowe wsparcie od wersji 10.0, bez zmian w 11.0
– IE Mobile – częściowe wsparcie od wersji 10.0
Według statystyk oznacza to, że ok. 60% zainstalowanych przeglądarek na świecie daje radę.
Polecam śledzić rozwój sytuacji na http://caniuse.com/#feat=contentsecuritypolicy
Przypuszczam, że jak w przypadku każdej nowej technologii wchodzącej w skład stacka HTML5 sytuacja z wersji na wersję przeglądarek będzie się poprawiać, przynajmniej jeśli chodzi o przeglądarki inne, niż IE ;)
@bl4de
Dzięki za wyczerpującą odpowiedź ;)
Pozdrawiam
Dla zainteresowanych: na ostatniej konferencji AppSec California 2014 Joel Weinberger z Google wygłosił prezentację na temat CSP:
https://www.youtube.com/watch?v=vpKB-yWn79U
Polecam.
„Raportowanie odbywa się poprzez wysyłanie przez przeglądarkę żądania metodą POST protokołu HTTP”
Można też ustawić powiadomienia mailowe, fajnie jest to tutaj opisane tutaj:
https://mathiasbynens.be/notes/csp-reports
ale przy dużych witrynach ze sporym ruchem lepiej tego nie robić, aby nie zarżnąć serwera ;)
A jeden duuuży polski bank się nie boi i unsafe-eval ma.
Witam
Coś sknociłem :(
Nie działa googletagmanager. Pomożecie Panowie?
Takie coś wstawilem w htaccess:
Header set Content-Security-Policy „default-src 'self’; img-src 'self’ data: http: https: *.anbank.pl; script-src 'self’ 'unsafe-inline’ ; style-src 'self’ 'unsafe-inline’ http: https: fonts.googleapis.com; font-src 'self’ data: http: https: fonts.googleapis.com themes.googleusercontent.com; script-src https://www.googletagmanager.com”
no i tak błąd ni się wyświetla:
Refused to load the script 'https://www.googletagmanager.com/gtm.js?id=GTM-NQ74F4C’ because it violates the following Content Security Policy directive: „script-src 'self’ 'unsafe-inline’ „. Note that 'script-src-elem’ was not explicitly set, so 'script-src’ is used as a fallback.
Nie umiem tego oogarnąć … bardzo proszę pomóżcie mi zrobić to jak należy
Dawno to robilem, wiec nie dam glowy, ale moze nie podoba mu sie WIELOkrotne zdefiniowanie script-src
Tzn. najpierw podajesz 'self’ 'unsafe-inline’
a potem osobno https://www.googletagmanager.com
Wpisalbym wszystkie dozwolone elementy w jednej definicji script-src
Nie wiem co zrobilam nie tak:
Header set Content-Security-Policy „script-src 'self’ http://www.google-analytics.com ajax.googleapis.com http://www.google.com google.com gstatic.com http://www.gstatic.com;”
WordPress wyrzuca mi w panelu wordpressa taki blad:
[Sprawdzanie stanu witryny wymaga obsługi języka JavaScript]
…w skutku pokazuje mi się tam ten tekst a nie normalny panel stanu i nie mam paska edycji tekstu przy próbie dodania wpisu.
Jak usuwam tą linie to oczywiście wszystko dziala.
Czy da sie to jakos przerobic zeby nie blokowalo mi elementow w panelu administracyjnym?
Witam.
Proszę o pomoc.
Chciałbym zablokować javaScript na podstronach bloga https://tuauta.pl/blog
Gdzie mam wstawić Content-Security-Policy: script-src 'none’