Preorder drugiego tomu książki sekuraka: Wprowadzenie do bezpieczeństwa IT. -15% z kodem: sekurak-book
Wszystko o CSP 2.0 – Content Security Policy jako uniwersalny strażnik bezpieczeństwa aplikacji webowej
Słowem wstępu
CSP zainteresowałem się kilka lat temu, gdy zacząłem zgłębiać bezpieczeństwo technologii skupionych wokół HTML5. Z nagłówkami Content-Security-Policy miałem styczność jako web developer oraz pentester. W zależności od roli projektowej i doświadczenia, CSP było moim przyjacielem lub wrogiem. Sądzę, że warto zaznajomić się z tą technologią i wyciągnąć z niej to, co najlepsze, by zabezpieczyć użytkowników aplikacji webowych, ale także – aby nie utknąć na zbyt dużych restrykcjach w czasie testów.
W sieci istnieje wiele poradników dotyczących CSP. Niestety te, na które trafiałem, zawsze miały wspólną wadę – nie odwoływały się do rekomendowanej wersji standardu (obecnie – drugiej). Autorzy poradników często mieszają mechanizmy proponowane przez W3C z niestandardową implementacją przeglądarek oraz nie wyjaśniają różnic. Z tego powodu projekty z CSP, z którymi miałem do czynienia, posiadały bardzo liberalne zasady nieznacznie podnoszące poziom bezpieczeństwa. Obserwowałem też wdrożenia CSP, które całkowicie nie działały lub powodowały popsucie aplikacji internetowej.
Aby pomóc innym programistom i testerom, zdecydowałem się napisać artykuł, którego źródłem są wyłącznie: dokumentacje W3C oraz moje zawodowe doświadczenie.
O czym jest ten artykuł
Artykuł pisany jest w kontekście zwiększania bezpieczeństwa („utwardzania”/hardeningu) aplikacji internetowych. Testowanie, omijanie i atakowanie CSP będzie tematem osobnej publikacji.
Content Security Policy 1.0 W3C Candidate Recommendation 15 November 2012 (wyłącznie do celów informacyjnych).
Content Security Policy Level 2, W3C Candidate Recommendation, 21 July 2015.
Ten materiał jest efektem wniosków z analizy fragmentów unormowanych oraz nienormatywnych dokumentacji W3C. W obu przypadkach analizowałem tylko stabilne, rekomendowane standardy (o statusie CR), które nie powinny zostać zmienione.
W momencie pisania tekstu, zostały rozpoczęte prace nad CSP 3. Ze względu na to, że niektóre elementy CSP 3 zostały już zaimplementowane w przeglądarkach, w artykule również się do nich odniosę. Z uwagi na możliwe zmiany w dokumentacji i rzetelność opracowania, referencje do trzeciej wersji CSP będą w niniejszym opracowaniu wyraźnie zaznaczone.
Czym jest CSP?
W chwili tworzenia aplikacji internetowej z reguły wiadomo, jakie zasoby trafią do przeglądarek końcowych użytkowników. Programiści nie powinni mieć większego problemu z określeniem, z jakiego miejsca zostaną pobrane pliki HTML, skrypty, style czy multimedia.
Można by więc pokusić się o wskazanie dozwolonych źródeł (domen, ścieżek, protokołów) dla różnych grup zasobów. Gdyby przeglądarka respektowała takie wartości, to użytkownicy aplikacji (oraz agresorzy), nie mogliby spowodować załadowania (złośliwych) zasobów (skryptów, apletów…) z domeny niewskazanej wprost przez programistę.
Content Security Policy (CSP) jest właśnie koncepcją wykorzystującą listy dozwolonych źródeł, z których mogą zostać załadowane zasoby strony. Programista tworzy białą listę hostów i ścieżek, którą dodaje do nagłówka odpowiedzi strony internetowej. Nowoczesna przeglądarka respektuje tę listę i – w razie naruszenia polityki – blokuje ładowanie oraz wykonywanie niechcianego zasobu.
Włączenie polityk CSP w znacznym stopniu utrudnia przeprowadzenie udanych ataków XSS, UI Redressing (Clickjacking), złośliwego wykorzystania ramek czy wstrzyknięć CSS. Gdy agresor będzie próbował dodać szkodliwe elementy do strony (np. przez nieznaną podatność), restrykcje CSP zatrzymają żądanie o wrogi zasób (np. skrypt). Nawet gdy w aplikacji zostanie odnaleziona podatność XSS, potencjalny atakujący może mieć bardzo utrudnione zadanie w jej wykorzystaniu, ponieważ ładunki („payloady”) podsyłane ofiarom nie załadują szkodliwych skryptów w miejscu wstrzyknięcia.
Pierwsze kroki z CSP
CSP można włączyć na dwa sposoby:
- przez specjalnie przygotowany nagłówek HTTP – Content-Security-Policy: $polityki-csp,
- przez dodanie znacznika meta w sekcji <head> kodu HTML – <meta http-equiv=”Content-Security-Policy” content=”$polityki-csp”>.
Zalecany jest pierwszy wariant – CSP włączone przez znacznik <meta> (dla bezpieczeństwa) nie interpretuje wszystkich dyrektyw.
Domyślną polityką, od której zaczyna się budowanie nagłówka, jest: blokuj wszystko. Programista modyfikując wartość CSP, rozluźnia obostrzenia dla konkretnych grup zasobów (np. osobno dla skryptów, obrazków itp.). Gdy nowoczesna przeglądarka, wspierająca użyte dyrektywy, odbierze wraz z treścią strony nagłówek:
Content-Security-Policy: default-src 'self' cdn.example.com; img-src img.example.com;
to podczas przetwarzania kodu HTML (a potem podczas działania strony) powodującego wysłanie żądań o skrypty, style czy fonty, pozwoli wykonać je wyłącznie do:
- tej samej domeny, co załadowana strona (’self’),
- do domeny cdn.example.com (korzystając z protokołu HTTP oraz HTTPS).
W powyższym przykładzie obrazki będą mogły zostać załadowane tylko spod adresu http(s)://img.example.com/*. Nie będzie możliwości wczytania zasobów z innych hostów. Wskazana polityka CSP zablokuje również wywoływanie skryptów umieszczonych między znacznikami <script></script>. Nie wykonają się również akcje z atrybutów takich jak onclick czy oninput, gdyż wykonanie tzw. „skryptów inline” musiałoby zostać dopuszczone przez dodanie 'unsafe-inline’:
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'
Jak widać, użycie CSP jest bardzo proste i intuicyjne. Zanim zapoznamy się z elementami, które możemy chronić, najpierw przyjrzyjmy się standardom stojącym za technologią oraz ich praktycznemu wsparciu w obecnych przeglądarkach.
Bałagan standardów i implementacji
Content Security Policy powstało z inicjatywy pracowników Google oraz Mozilli. Od samego początku przeglądarki tych dwóch firm najszybciej wprowadzały nowe rozszerzenia CSP, oczywiście każda w innym czasie. Po kilku latach firma Microsoft – jakby pod naciskiem konkurencji – również podjęła pierwsze próby implementacji CSP w swoich produktach. Ostatecznie pomysł na nowy standard trafił pod skrzydła grupy roboczej W3C.
Z początkiem 2016 roku CSP w wersji 1.0 zostało całkowicie zastąpione przez wersję drugą. Można również zapoznać się ze szkicem (W3C draft) trzeciej wersji standardu, uzupełnianej o nowe mechanizmy zabezpieczeń.
Content Security Policy w pierwszej wersji zostało bardzo dobrze przyjęte przez inżynierów bezpieczeństwa. Nieco gorzej wypadło w oczach web developerów, ponieważ w niektórych przypadkach, CSP okazało się bardziej skomplikowane niż przypuszczano. Dlatego też szybko rozpoczęto prace nad usprawnieniami, oznaczonymi numerem 1.1. Standard powoli się rozrastał, aż W3C zdecydowało się zmienić numerację na Level 2, zamrozić proces zmian i kontynuować pracę nad wersją trzecią.
Mogłoby się wydawać, że prace nad CSP przebiegały bardzo sprawnie – przeglądarki szybko implementowały kolejne dyrektywy CSP, a grupy robocze płynnie tworzyły nowe mechanizmy zabezpieczeń. Problem pojawił się w innym miejscu – po stronie użytkowej, kiedy programiści chcieli się nauczyć, jak korzystać z CSP oraz przekazać tę wiedzę dalej. Pojawiły się więc poradniki oraz benchmarki, do których – niestety – wkradło się wiele niespójności.
Do niedawna jeszcze mieliśmy niemały bałagan w samej standaryzacji i stopniu jej implementacji; na szczęście proces zaczyna się porządkować, niemniej nadal można trafić na wdrożenia CSP pisane w duchu wsparcia:
- nieustandaryzowanych wersji nagłówków (np. X-Content-Security-Policy) lub niestandardowych dyrektyw (np. allow zamiast default-src),
- CSP 1.0,
- wybranych elementów CSP 1.1,
- wczesnej (rozwijanej) wersji CSP 2.0,
- oficjalnej (stabilnej) wersji CSP 2.0,
- wybranych elementów CSP 3.0.
Jak uniknąć problemów
Aby uniknąć problemów w przyszłości, przed rozpoczęciem definiowania polityk CSP należy:
- jasno ustalić wymagania dotyczące wersji wspieranych przeglądarek przez klientów aplikacji webowych,
- najważniejsze polityki opierać na stabilnej wersji standardu – obecnie „W3C Level 2 Candidate Recommendation”,
- przeanalizować, czy dyrektywy z nowszych szkiców W3C mogą zwiększyć bezpieczeństwo klientów końcowych; jeśli tak – warto je wprowadzić, ale z zastrzeżeniem, że w przyszłości ich wsparcie może się zmienić,
- wykonać testy wsparcia najważniejszych dyrektyw dla kluczowych przeglądarek.
Wsparcie w przeglądarkach
Wsparcie nowych technologii WWW w przeglądarkach można sprawdzić w serwisie „Can I use”. Pod adresem http://caniuse.com/#search=CSP znajdziemy podstawowe informacje o stopniu implementacji CSP w różnych przeglądarkach.
Wersje CSP 1.0 oraz 2.0 nie powinny się już zmieniać, więc http://caniuse.com jest dość dobrym (i często aktualizowanym) źródłem wiedzy. Trzeba tylko pamiętać o adnotacjach i wyszukiwać informacji o pełnym wsparciu i ze szczególną rezerwą podchodzić do przeglądarek z „częściowym wsparciem” – na przykład IE 10/11 w praktyce implementuje tylko jedną dyrektywę CSP 1.0 (sandbox), w dodatku przez niestandardowy nagłówek. Dość często Internet Explorer okazuje się więc niemiłą niespodzianką.
Pamiętając o tego rodzaju problemach, przyjrzyjmy się ogólnie, w jakich przeglądarkach możemy liczyć na wsparcie Content Security Policy.
Wsparcie CSP 1.0
Obecnie poziom wsparcia pierwszej wersji standardu jest bardzo wysoki. Kompletna implementacja standardu W3C została wdrożona w:
- Internet Explorer Edge (12+),
- Firefox (23+),
- Chrome (25+),
- Safari (7+),
- a także w mobilnych wersjach: Safari, Android Browser/Web View/Chrome.
Problemem jest wyłącznie Internet Explorer 10/11, który obsługiwany jest tylko przez nagłówek 'X-Content-Security-Policy’ i respektuje wyłącznie dyrektywę sandbox. Inne dyrektywy, takie jak chociażby script-src czy style-src, nie są interpretowane przez Internet Explorer w wersjach poniżej „Edge”.
Można założyć, że Internet Explorer w wersji 10 oraz 11 (i starszych) jest przeglądarką niewspierającą CSP.
Wsparcie CSP 2.0
Na początku 2016 roku, kompleksowym wsparciem CSP 2.0 może pochwalić się tylko Google Chrome i przeglądarki oparte na jej silniku (Opera, Opera Mobile, Android WebView, Android Chrome). Nieźle radzi sobie również Firefox, który od wersji 36 ma problemy tylko z interpretacją plugin-types oraz child-src. Za kilka miesięcy z pewnością „dogoni” przeglądarkę Google, zapewniając stuprocentowe wsparcie CSP 2.0.
Internet Explorer, Internet Explorer Edge, Safari oraz wersje mobilne tych przeglądarek na początku 2016 roku nie wspierały CSP 2.0.
Wsparcie CSP 3
W celu szybszego wdrożenia wersji drugiej, W3C zdecydowało się na wyrzucenie niektórych dyrektyw CSP do kolejnej wersji standardu. Część z nich została już zaimplementowana przez twórców przeglądarek, więc możemy mówić o pierwszych wdrożeniach CSP Level 3.
Trudno jednak obecnie wyciągać wnioski o poziomie wsparcia CSP 3.0 w przeglądarkach, ponieważ standard ciągle ewoluuje. Niektóre z nowości są bardzo ciekawe i można pokusić się o ich wdrożenie – ale należy pamiętać, że z dnia na dzień może zmienić się sposób ich działania.
Więcej o nowinkach CSP Level 3 w sekcji: „Coś się kończy, coś zaczyna – CSP 3.0?”.
Składnia polityk
Content-Security-Policy jest nagłówkiem odpowiedzi serwera, którego wartością są polityki stopniowo rozluźniające zasadę „blokuj wszystko”.
Polityka CSP (CSP policy) składa się z dwóch elementów:
- dyrektywy (directive),
- list źródeł (source list).
Ogólny schemat polityki (dyrektywy) wygląda następująco:
directiveX: source1 source2 sourceN;
Poniższy zapis definiuje więc trzy polityki:
Content-Security-Policy: script-src 'self'; img-src 'self'; style-src 'self' cdn.com;
Dyrektywy rozdziela się średnikami, białe znaki nie mają znaczenia. Powyżej widzimy restrykcje dla skryptów, obrazków oraz styli. Nie zmieniono definicji domyślnego zachowania, więc inne elementy w takim przypadku (np. fonty) byłyby blokowane.
Dyrektywy
Dyrektywami są słowa kluczowe, które opisują reguły dostępu do zasobu. Gdy przeglądarka nie wspiera danej dyrektywy, pominie ją i zacznie przetwarzać następną (czyli w przypadku braku wsparcia, przeglądarka nie przestaje nagle interpretować CSP).
Poniżej znajduje się lista dyrektyw oraz informacja, od której wersji standardu dana dyrektywa powinna być wspierana:
- base-uri (CSP 2.0),
- child-src (CSP 2.0, zastępuje frame-src),
- connect-src (CSP 1.0),
- default-src (CSP 1.0),
- font-src (CSP 1.0),
- form-action (CSP 2.0),
- frame-ancestors (CSP 2.0),
- frame-src (tylko CSP 1.0, obecnie przestarzałe),
- img-src (CSP 1.0),
- media-src (CSP 1.0),
- object-src (CSP 1.0),
- plugin-types (CSP 2.0),
- report-uri (CSP 1.0),
- sandbox (CSP 1.0),
- script-src (CSP 1.0),
- style-src (CSP 1.0).
Lista nr 1. Oficjalna lista dyrektyw CSP 1.0 oraz CSP 2.0.
Istnieje też kilka bardzo ciekawych dyrektyw, które ostatecznie wypadły z CSP 2.0, ale mogą trafić do trzeciej wersji CSP:
- upgrade-insecure-requests,
- block-all-mixed-content,
- manifest-src,
- referrer,
- reflected-xss.
Lista nr 2. Wybrane dyrektywy, które mogą wejść w skład nowszych wersji CSP.
W sekcji „Coś się kończy, coś zaczyna – CSP 3.0?” przyjrzymy się tym dyrektywom dokładniej.
Lista źródeł dyrektywy
Na listę źródeł dyrektywy może trafić:
- host:
- niepoprzedzony protokołem (oznacza wtedy dopasowanie z HTTP oraz HTTPS) – example.com;
- poprzedzony protokołem – https://example.com;
- wraz z definicją portu – example.com:8080;
- schemat, domena i/lub port może zawierać znak wieloznaczny * (ale w części hosta musi znajdować się skrajnie z lewej strony) – https://*.example.com:*;
- host wraz ze ścieżką (od CSP 2.0):
- http://example.com/js/plik.js – dopasowanie do pliku plik.js znajdującego się pod adresem http://example.com/js/plik.js;
- example.com/js – pozwala ładować tylko pojedynczy plik js w domenie example.com (nie dotyczy katalogu);
- example.com/js/ – pasuje do całego katalogu js/ w domenie example.com
- ’none’ (zabroń);
- ’self’ (pozwolenie dla tego samego hosta, zgodnie z zasadą Same Origin Policy);
- ’unsafe-inline’ (pozwalaj na skrypty, style, obrazki definiowane bezpośrednio w kodzie HTML);
- ’unsafe-eval’ (pozwalaj na stosowanie niebezpiecznych funkcji typu eval);
- nonce-XXXX – o tym w sekcji „Funkcje zaawansowane › nonce” (CSP 2.0);
- sha256-XXXX, sha364-XXXX lub sha512-XXXX (od CSP 2.0) – o tym w sekcji „Funkcje zaawansowane › Inline Hash”;
- specyficzna fraza dla danej dyrektywy (dokładny opis w szczegółach dyrektywy).
Lista nr 3. Wartości, które mogą trafić na listę źródeł dyrektyw.
Warto wspomnieć o kilku najczęstszych błędach w implementacji dyrektyw:
- przy politykach default-src 'self’; img-src cdn.com; obrazki zostaną załadowane tylko z domeny cdn.com (a nie z tej samej domeny oraz z domeny cdn.com);
- odwołanie do obrazka <img src=”//127.0.0.1/vizz.png”/> nie zadziała, gdy dozwolonym hostem będzie localhost;
- gdy przy polityce img-src localhost użyjemy <img src=’/vizz.png’ />, to obrazek wyświetli się tylko podczas odwiedzenia adresu http://localhost (nie zadziała w przypadku http://127.0.0.1);
- zapis hostu lub ścieżki w cudzysłowie jest błędny – przy img-src 'localhost’ nie będziemy w stanie wyświetlać żadnych obrazków.
Szczegółowy opis dyrektyw
default-src
Domyślna polityka CSP blokująca dostęp do niezdefiniowanego źródła może zostać zmieniona przy pomocy dyrektywy default-src.
Gdy mamy wpływ na CSP w trakcie tworzenia aplikacji webowej (np. jako programista), dobrym pomysłem jest rozpoczęcie od pojedynczej, restrykcyjnej dyrektywy default-src (’none’ lub 'self’), a następnie „rozluźnianie” obostrzeń przez uszczegóławianie źródeł skryptów, obrazków itp. Dla zachowania czytelności, warto też dodawać default-src jako pierwszą politykę, nawet, gdy jej wartością miałoby być słowo kluczowe 'none’.
Należy zaznaczyć, że po definicji default-src, wartości domyślne ustawiane są tylko na wybrane i niezdefiniowane dyrektywy.
Poniżej lista dyrektyw, które mogą przybrać wartości domyślne (według CSP 2.0):
- child-src (tylko w CSP 2.0! zastępuje frame-src),
- connect-src,
- font-src,
- frame-src (przestarzałe!),
- img-src,
- media-src,
- object-src,
- script-src,
- style-src.
Lista nr 4. Dyrektywy, które mogą przyjąć wartości domyślne.
base-uri (CSP 2.0; nie przyjmuje wartości default-src)
Ta dyrektywa odnosi się do ograniczeń nakładanych na element <base>. Tag „base” w HTML pozwala określić:
- bazowy adres URL dla linków względnych,
- oraz domyślny kontekst nawigacji (np. target=_blank otwierający odnośnik w nowym oknie).
Dodanie lub manipulacja wartości znacznika „base” przez atakującego może skutkować załadowaniem złośliwych zasobów (np. skryptów) z obcych źródeł. Definiując wartość base-uri, możemy znacznie utrudnić ataki wykorzystujące ten element.
child-src (CSP 2.0; może przyjąć wartość domyślną)
Dotyczy dodatkowych (zagnieżdżonych) kontekstów przeglądarki (nested browsing context), czyli elementów takich jak ramki frame/iframe oraz „web workerzy” HTML5 (konkretnie są to restrykcje konstruktora Worker oraz SharedWorker).
Zastępuje frame-src z CSP 1.0.
connect-src (CSP 1.0; może przyjąć wartość domyślną)
Pozwala określić, co może być celem żądań asynchronicznych XMLHttpRequest send(), konstruktorów WebSocket, EventSource lub parametrów funkcji sendBeacon() technologii Beacon.
Dyrektywa ta wpłynie między innymi na takie wywołania w kodzie, jak:
- (new XMLHttpRequest()).open(’GET’, 'http://example.com’, true);
- new WebSocket(’wss://example.com’);
- new EventSource(’https://evil.com’);
font-src (CSP 1.0; może przyjąć wartość domyślną)
Tu możemy określić ograniczenia dla fontów webowych, czyli np. źródeł, które pojawiają się w definicji @font-face w stylach CSS.
form-action (CSP 2.0; nie przyjmuje wartości default-src)
Obostrzenia dla atrybutu „action” formularza HTML.
frame-ancestors (CSP 2.0; nie przyjmuje wartości default-src)
Restrykcje zagnieżdżania zasobu w innych miejscach. Dotyczy to ramkowania strony przez takie elementy, jak ramki frame/iframe czy elementy <object>, <embed>, <applet> i podobne.
Obecnie jest to jedno z najlepszych zabezpieczeń przeciwko atakom UI Redressing (w szczególności Clickjacking) oraz innym zagrożeniom, których etapem jest wyświetlenie atakowanej strony wewnątrz ramki.
Domniemaną wartością frame-ancestors jest * (niezależnie od default-src, który nie wpływa na tę dyrektywę). Oznacza to, że strony domyślnie mogą być zagnieżdżane/ramkowane w innych miejscach.
- Dyrektywa frame-ancestors w CSP 2.0 zastępuje nie do końca ustandaryzowany nagłówek X-Frame-Options, który potrafił sprawiać problemy w mocno rozproszonych środowiskach.
- Gdy przeglądarka otrzyma dwa nagłówki: Content-Security-Policy z polityką
frame-ancestors oraz X-Frame-Options, wykorzystana będzie wyłącznie wartość z CSP.
Na liście dozwolonych źródeł frame-ancestors może się znaleźć:
- ’none’ – strona nie pozwala na ramkowanie/zagnieżdżanie (odpowiednik X-Frame-Options: deny);
- ’self’ – strona pozwala na ramkowanie/zagnieżdżanie tylko wtedy, gdy robią to elementy spod tego samego protokołu, adresu i portu (odpowiednik X-Frame-Options: sameorigin);
- host – wówczas strona pozwoli na zagnieżdżenie swojej zawartości przez wskazanego hosta – stosuje się tutaj standardowy zapis hosta CSP (patrz: lista nr 3), np. frame-ancestors trusted.ads-partner.com (nie było takiej możliwości w X-Frame-Options).
frame-src (CSP 1.0, przestarzałe w CSP 2.0; może przyjąć wartość domyślną)
Starsza, prostsza wersja child-src. Działa podobnie, ale wyłącznie dla ramek iframe/frame. Dyrektywa child-src działa na większą grupę elementów i powinna być używana zamiast frame-src, która oznaczona jest jako „deprecated” i prawdopodobnie zniknie w trzeciej wersji standardu.
img-src (CSP 1.0; może przyjąć wartość domyślną)
Lista dozwolonych źródeł obrazków, które mogą być podane w:
- znaczniku <img src>,
- znaczniku <input type=image>,
- atrybucie poster elementu <video>,
- atrybucie href elementu <link> służącym m.in. do ustawiania ikony strony,
- wartości funkcji url(), image(), image-set() (i podobnych) w CSS,
- innych źródłach obrazków, które w przyszłości będą obsługiwane przez przeglądarki.
media-src (CSP 1.0; może przyjąć wartość domyślną)
Ograniczenia dla dozwolonych źródeł multimediów (filmy, dźwięki, ścieżki…) wskazywanych przez atrybut src wewnątrz tagów <video>, <audio>, <source> oraz <track>.
object-src (CSP 1.0; może przyjąć wartość domyślną)
Restrykcje dla źródeł, z których mogą być pobierane obiekty obsługiwane przez pluginy przeglądarki. Dotyczy to między innymi apletów Flash, Silverlight oraz Java.
Content Security Policy w tej dyrektywie sprawdza:
- znaczniki <object> oraz <embed>, które mogłyby zostać użyte do stworzenia zagnieżdżonego kontekstu przeglądarki (tak jak robią to ramki);
- źródło atrybutów <object data> oraz <embed src>,
- źródło atrybutów <applet code> oraz <applet archive>.
plugin-types (CSP 2.0; nie przyjmuje wartości default-src)
Za pomocą tej dyrektywy możemy kontrolować rodzaj pluginów uruchamianych przez przeglądarkę do obsługi zasobów takich jak aplety Flash czy Silverlight, zagnieżdżanych przy pomocy znaczników <object> oraz <embed>.
W odróżnieniu od object-src wskazującego skąd obiekt może być załadowany, plugin-types służy do wskazania dozwolonych typów MIME. Gdy wartością dyrektywy będzie:
Content-Security-Policy: plugin-types application/pdf application/x-shockwave-flash
wtedy na odwiedzanej stronie przeglądarka będzie mogła uruchomić wyłącznie plugin do obsługi plików PDF oraz apletów Flash, ale nie będzie mogła obsłużyć apletów Javy oraz Silverlight.
Po włączeniu restrykcji należy upewnić się, że deklaracje obiektów w kodzie HTML mają ściśle zdefiniowany typ MIME – w przeciwnym wypadku nie zostaną załadowane, niezależnie od zawartości:
<object data='resource' type='application/pdf'></object>
Ze względu na nietypowy format listy źródeł domyślna polityka default-src nie wpływa na plugin-types. Na liście typów MIME nie mogą również pojawić się znaczniki wieloznaczne *.
report-uri (CSP 1.0; nie przyjmuje wartości default-src)
Jest to specjalna dyrektywa, która wskazuje, pod jaki adres powinien zostać wysłany raport w przypadku naruszenia zasad CSP. Wartością dyrektywy powinien być adres URI.
Więcej o raportowaniu w sekcji „Funkcje zaawansowane › Raportowanie”.
sandbox (CSP 1.0; nie przyjmuje wartości default-src)
Użycie tego słowa kluczowego w CSP spowoduje wyświetlenie całego zasobu (odwiedzanej strony) w trybie piaskownicy HTML5. Tryb ten, pierwotnie wprowadzony jako mechanizm ochrony ramek, pozwala na wyłączenie niektórych mechanizmów przeglądarki.
Gdy zasób jest wyświetlany w trybie „sandbox”, wtedy nakładane są nie niego wszystkie poniższe obostrzenia:
- wysyłanie formularzy jest wyłączone,
- konteksty przeglądania (np. ramki) zawsze są limitowane ścisłą zasadą Same Origin Policy,
- wykonywanie skryptów zostaje wyłączone,
- zmiana nawigacji jest zabroniona (window top navigation),
- zabrania się otwierania wyskakujących okienek (pop-up),
- interfejs do blokowania wskaźnika myszy zostaje wyłączony.
Lista nr 5. Restrykcje trybu sandbox w HTML5.
Wszystkie powyższe ograniczenia zostaną wprowadzone, gdy słowo kluczowe sandbox trafi na listę CSP:
Content-Security-Policy: sandbox;
Możliwe jest rozluźnienie niektórych obostrzeń trybu piaskownicy. Aby to zrobić, wystarczy na listę dozwolonych źródeł dyrektywy sandbox dodać wybrane słowa kluczowe z poniższej listy (dowolny zestaw oddzielony spacjami, bez cudzysłowów):
- allow-forms
- allow-same-origin
- allow-scripts
- allow-top-navigation
- allow-popups
- allow-pointer-lock
W poniższym przykładzie:
Content-Security-Policy: sandbox allow-scripts allow-popups;
załadowana strona będzie mogła wykonywać kod Javascript oraz tworzyć okna pop-up. Niezależnie od tego, czy logika wysyłania formularzy zostanie zaimplementowana przez kod HTML czy Javascript, ich wysłanie zostanie zabronione (nawet przy włączonym Javascript). Na stronie nie będzie również możliwości zmiany adresu ramkującej (nadrzędnej) strony (zmiana adresu kontekstu nadrzędnego przez window.top.location.href).
Szczegółowe informacje o HTML5 Sandboxing można przeczytać na stronach W3C.
script-src (CSP 1.0; może przyjąć wartość domyślną)
Sztandarowa dyrektywa CSP, która włącza restrykcje dla skryptów na stronie. Na liście źródeł tej dyrektywy mogą pojawić się:
- dozwolone hosty (CSP 1.0), a nawet ścieżki (CSP 2.0);
- słowa kluczowe 'none'(zabroń) lub 'self’ (zezwól tylko z tego samego źródła);
- słowo kluczowe 'unsafe-inline’, które pozwoli wykonywać skrypty „inline” ;
- słowo kluczowe 'unsafe-eval’, które pozwoli wykonywać (ewaluować) w sposób dynamiczny kod Javascript (wpływa na wywołania funkcji takich jak eval(), setTimeout(), setInterval() oraz na konstruktor funkcji Function).
Użycie script-src bardzo utrudnia udane wykorzystanie błędów XSS. Lista dozwolonych hostów utrudni atakującemu dołączenie złośliwego skryptu ze swojej domeny. Brak wartości 'unsafe-inline’ utrudni wykonanie skryptów „inline”, które są najpopularniejszą metodą dystrybucji ładunku XSS. Brak wartości 'unsafe-eval’ utrudni zaś eksploatację błędów DOM XSS.
style-src (CSP 1.0; może przyjąć wartość domyślną)
Jest to bardzo często używana dyrektywa, która działa analogicznie do script-src, tylko dotyczy kaskadowych arkuszy stylów – czyli plików CSS, oraz styli definiowanych wprost w kodzie HTML („inline”).
Funkcje zaawansowane
Raportowanie (CSP 1.0)
Po dodaniu nagłówka Content-Security-Policy przeglądarki od razu zaczną respektować wdrożone zasady i chronić użytkowników aplikacji webowej.
Niestety, o przeoczenia w CSP nietrudno – chociażby podczas odwoływania się do bibliotek hostowanych w sieciach CDN tylko na niektórych podstronach serwisu. W przypadku błędów, przeglądarki zazwyczaj zablokują zbyt dużo elementów na stronie, utrudniając korzystanie z serwisu. Czym aplikacja większa i architektura bardziej rozproszona, tym bardziej zbyt wysokie restrykcje mogą utrudnić, a nawet całkowicie uniemożliwić korzystanie z serwisu.
Remedium na tego rodzaju problemy jest tryb raportowania, który włącza się przez osobny nagłówek HTTP w celach testowych, jeszcze przed głównym wdrożeniem CSP:
Content-Security-Policy-Report-Only: politykiCSP;
Przeglądarka, odbierając powyższy nagłówek, będzie przetwarzała podane polityki, ale w momencie ich naruszenia nie zablokuje zasobu, zwracając błąd w konsoli Javascript (rysunek 3.).
Aby rozszerzyć funkcje raportowania, warto użyć dyrektywy report-uri, dzięki czemu podczas naruszenia restrykcji, pod wskazany adres zostanie wysłany raport o tym zajściu. Raport jest zwykłym obiektem JSON, który należy odebrać we własnym serwisie webowym i np. zapisać w celu dalszej analizy.
Przykładowo – podczas naruszenia poniższych zasad:
Content-Security-Policy: default-src 'self'; report-uri http://example.org/csp-report.cgi
spowodowanych przez próbę załadowania obrazka http://evil.example.com/image.png, raport wysłany przez przeglądarkę przybierze postać przedstawioną na listingu 1.
Listing 1. Przykład raportu wysyłanego do serwisu webowego podczas naruszenia polityki CSP.
{ "csp-report": { "document-uri": "http://example.org/page.html", "referrer": "http://evil.example.com/haxor.html", "blocked-uri": "http://evil.example.com/image.png", "violated-directive": "default-src 'self'", "effective-directive": "img-src", "original-policy": "default-src 'self'; report-uri http://example.org/csp-report.cgi" } }
Dostaniemy więc szczegółowe informacje o źródle problemu, polityce, która spowodowała blokadę, a nawet całą zawartość (bardzo przydatne, gdy nagłówek jest generowany dynamicznie przez serwer).
Tryb raportowania nie ogranicza się wyłącznie do debugowania poprawności mechanizmu CSP podczas rozwoju oprogramowania czy wdrożenia. Poniżej podaję dwa przykłady, które pokazują, w jaki sposób można połączyć standardowy tryb „CSP”, tryb „Report-Only” oraz report-uri.
CSP jako IDS
Użycie dyrektywy report-uri połączone z systemem składowania raportów ich automatycznej analizy może posłużyć jako prosty system wykrywania włamań (głównie w kontekście nieznanych luk XSS). Raport, który zawiera naruszenia w rodzaju „próba wykonania skryptu inline” lub „ewaluacji funkcji Javascript”, może okazać się bardzo cennym sygnałem dla zespołu developerskiego.
Dyrektywę report-uri można wprowadzić niezależnie od tego, czy w projekcie istnieje możliwość użycia standardowego CSP (w trybie blokowania) czy też nie. Jeżeli programiści nie mogą jeszcze wdrożyć CSP (ze względu na ryzyko błędnego działania strony), to nic nie stoi na przeszkodzie, aby inżynier bezpieczeństwa zaproponował restrykcyjne polityki działające w trybie report-only.
Bezstresowe wdrożenie CSP
Największą skuteczność wdrażania CSP uzyskujemy wtedy, gdy zaczynamy nowy projekt z bardzo rygorystycznymi zasadami stopniowo rozluźnianymi w trakcie rozwoju oprogramowania. Niestety nie zawsze mamy taką możliwość.
Gdy celem hardeningu z wykorzystaniem CSP jest duży serwis, którego działanie nie może zostać zakłócone, dobrym pomysłem jest rozpoczęcie od bardzo pobłażliwych polityk. Potem przy pomocy Content-Security-Policy-Report-Only wprowadzamy ostrzejsze restrykcje, analizujemy raporty potencjalnych błędów i wprowadzamy poprawki. Gdy raporty przestaną napływać, możemy zastąpić wartość CSP zawartością trybu raportowania i zacząć całą operację od nowa, testując jeszcze większe obostrzenia.
Po kilku iteracjach powinniśmy mieć dopracowane reguły w nagłówku CSP, które nie spowodują problemów na stronie.
Nonce (CSP 2.0)
Content Security Policy pokazuje największą siłę, gdy blokuje wywołanie kodu „inline”. Niestety, niekiedy przepisanie takich wstawek kodu może okazać się bardzo skomplikowane. Dlatego też w CSP 2.0 (pierwotnie w CSP 1.1) wprowadzono mechanizmy „nonce”(oraz „Inline Hash”), pozwalające na działanie wybranych skryptów (styli) osadzonych bezpośrednio w kodzie strony.
Mechanizm „nonce” wprowadza się w dwóch krokach.
Po pierwsze należy dodać go do dyrektywy skryptów (styli) przez słowo kluczowe 'nonce-$RANDOM’ (w pojedynczych cudzysłowach), zastępując wartość $RANDOM trudnym do przewidzenia tokenem (analogicznie jak w przypadku tokenów anty-CSRF).
Następnie tę samą wartość umieszcza się w atrybucie nonce znacznika <script> lub <style>, których wykonanie chcemy dopuścić.
Kilka przykładów zastosowania dyrektywy „nonce” przedstawiono na listingu 2.
Listing 2. Przykład zastosowania derektywy „nonce” z CSP 2.0.
Content-Security-Policy: default: 'self'; script-src 'self' example.com 'nonce-Nc3n83cnSAd3wc3Sasdfn939hc3' <script> alert("Skrypt zostanie zablokowany, ponieważ polityka skryptów nie zawiera 'unsafe-inline'.") </script> <script nonce="EDNnf03nceIOfn39fn3e9h3sdfa"> alert("Zablokowane, ponieważ nonce polityki skryptów ma inną wartość") </script> <script nonce="Nc3n83cnSAd3wc3Sasdfn939hc3"> alert("Skrypt zostanie wykonany, ponieważ wartość nonce deklaracji skryptu zgadza się z wartością w polityce CSP") </script> <script src="https://example.com/ZEZWOLONY-ze-wzgledu-na-script-src.js"></script> <script nonce="EDNnf03nceIOfn39fn3e9h3sdfa" src="https://elsewhere.com/ZABLOKOWANY-ze-wzgledu-na-bledny-nonce.js"></script> <script nonce="Nc3n83cnSAd3wc3Sasdfn939hc3" src="https://elsewhere.com/DOZWOLONY-z-zewnetrznego-zrodla-ze-wzgledu-na-zgodnosc-nonce.js"></script>
Warto zaznaczyć, że mechanizm „nonce” nie musi służyć tylko do tzw. walki ze skryptami i stylami „inline”. Ostatni przykład z listy powyżej pokazuje, w jaki sposób, odwołując się do pojedynczych skryptów z CDN, można utrzymać silną, zwięzłą politykę CSP.
Inline Hash (CSP 2.0)
Jest to kolejny pomysł (obok „nonce”) na wskazanie zaufanego skryptu lub stylu „inline”. W tym wypadku – przy pomocy jednokierunkowych funkcji skrótu.
Gdy do listy dozwolonych źródeł skryptów/styli dodamy wartość 'sha256-$base64hashInline’ (pojedyny cudzysłów), przeglądarka dopuści wykonanie kodu umieszczonego między tagami <script></script> (<style></style>), jeśli tylko hash SHA256 zawartości znaczników będzie równy wartości $base64hashInline (skrót musi być zakodowany algorytmem base64).
Obsługiwane algorytmy jednokierunkowych funkcji skrótu to:
- sha256,
- sha384,
- sha512.
Listing 3. przedstawia sposób użycia mechanizmu Inline Hash:
Listing 3. Przykład zastosowania Inline Hash.
<!-- Przykład CSP dla skrótu sha512 ciągu: alert('Hello, world.'); --> Content-Security-Policy: default-src 'self' script-src 'self' 'sha512- Q2bFTOhEALkN8hOms2FKTDLy7eugP2zFZ1T8LCvX42Fp3WoNr3bjZSAHeOsHrbV1Fu9/A0EzCinRE7Af1ofPrw==' <!-- Zasady zdefiniowane w CSP dopuszczą wykonanie poniższego kodu HTML: --> <script>alert('Hello, world.');</script> <!-- Następne trzy wstawki zostaną zablokowane: --> <script>alert(/XSS/);</script> <script> alert('Hello, world.'); </script> <script> alert('Hello, world.');</script>
echo -n „alert(’Hello, world.’);”
| openssl dgst -sha256 -binary | openssl enc -base64
echo -n „alert(’Hello, world.’);”
| openssl dgst -sha256 -binary | openssl enc -base64 –A > hash.txt
Drugim sposobem dostarczenia wartości skrótu do CSP jest przeczytanie opisu błędu naruszenia zasad CSP dla skryptu „inline” i skopiowanie proponowanej wartości do polityki, tak jak pokazano na poniższym rysunku:
„Coś się kończy, coś zaczyna…” – CSP 3.0?
Poniżej wymieniam kilka dyrektyw, które zostały zaimplementowane w niektórych przeglądarkach i mogą trafić jako element standardu CSP Level 3:
- upgrade-insecure-requests – włącz mechanizm podmieniający wszystkie odwołania do zasobów HTTP do ich wersji HTTPS;
- block-all-mixed-content – zabroń wczytywania zasobów przez niezaufane medium (HTTP), gdy cała strona została załadowana po HTTPS;
- manifest-src – restrykcje dotyczące pliku Web App Manifest, który umożliwia zarządzanie różnymi metadanymi aplikacji webowych, co pozwala je upodobnić do aplikacji natywnych (przy braku definicji respektuje default-src);
referrer – zwiększa kontrolę nad danymi pojawiającymi się w nagłówku Referer, który może spowodować wysłanie wrażliwych danych do obcej domeny. Obecnie proponowane wartości tej dyrektywy to: - no-referrer – nigdy nie wysyłaj nagłówka,
- no-referrer-when-downgrade – nie wysyłaj nagłówka, gdy nawigacja przechodzi ze strony HTTPS na HTTP (podczas nawigacji HTTPS›HTTPS, HTTP›HTTP oraz HTTP›HTTPS nagłówek będzie dodawany),
- origin – dodawaj tylko nazwę hosta w nagłówku referrer (chroni przed wyciekiem informacji znajdujących się w ścieżce oraz tzw. „query stringu”),
- origin-when-cross-origin – gdy nawigacja będzie w ramach tej samej domeny (niezależnie od protokołu), wtedy referrer będzie zawierał pełny URL; gdy nawigacja będzie się odbywać między różnymi domenami, referrer będzie zawierał tylko nazwę hosta,
- unsafe-url – wysyłaj pełny URL w nagłówku referrer (czyli brak restrykcji);
- reflected-xss – celem dyrektywy jest zastąpienie niestandardowego nagłówka X-XSS-Protection, który instruuje przeglądarkę o włączeniu mechanizmów „Anti-Reflected-XSS” (czyli „XSS Auditor” w Google Chrome czy jego odpowiednik w Internet Explorer); możliwe wartości to:
- allow (wyłącz zabezpieczenie anti-xss),
- block (blokuj wykonywanie wykrytego skryptu),
- filter (postaraj się wyłącznie filtrować złośliwy skrypt).
Czy warto stosować CSP?
Content Security Policy jest szybko rozwijającą się, łatwą w zrozumieniu technologią, która skutecznie utrudnia życie crackerom. Pokazuje największą moc wówczas, gdy jest rozwijana w projektach od samego początku – rygorystyczne polityki znacznie zwiększą bezpieczeństwo, a wyższa jakość kodu zmniejsza ogólną liczbę błędów.
Nie ma jednak róży bez kolców. Pobieżne zrozumienie standardu lub zgubienie się w szczegółach dokumentacji, może unieruchomić działanie serwisu. Wdrożone zasady należy przetestować w najważniejszych przeglądarkach – najlepiej przy pomocy CSP w trybie raportowania.
CSP nigdy nie może zastąpić bezpiecznego kodu – nowe ograniczenia pomagają zmniejszać skutki ataków (takich jak XSS), ale nie są mechanizmami do zapobiegania im!
Dużym problemem podczas wdrożenia CSP są również biblioteki Javascript implementujące tzw. „client–side MVC/MVVM”, które przestają działać przy rygorystycznych politykach. Z popularnych frameworków jedynie AngularJS od dawna może pochwalić się wsparciem CSP, jednak jest to raczej wyjątek potwierdzający regułę. Miejmy nadzieję, że konkurencja szybko pójdzie za przykładem frameworka firmy Google.
Podsumowanie
Wiedza o Content Security Policy szybko rozpowszechnia się wśród programistów i testerów. Niestety pobieżne zrozumienie CSP może skutkować wprowadzaniem liberalnych polityk, tylko nieznacznie podnoszących poziom bezpieczeństwa. Zachęcam do dokładnej analizy tego standardu i wdrożenia go do projektów webowych, aby skutecznie utrudnić życie potencjalnym agresorom.
Content Security Policy staje się uniwersalnym narzędziem konfiguracyjnym zabezpieczenia strony w przeglądarkach użytkowników. Z nadzieją czekam na kolejne wersje standardu oraz wdrożenia.
–vizzdoom
Nie żeby coś, ale błąd z opisem manifestu z CSP 3.0 dalej jest… Obiecaliście, że poprawicie – nieładnie, nieładnie ;)
Polecam do testów https://report-uri.io oraz https://securityheaders.io
Świetny art! Poza jedną małą wtopą: wyjątki zaprzeczają regułom ;)
W ostatniej liście („Coś się kończy, coś zaczyna…”) trochę Wam się punktowanie rozjechało.
Poza tym, świetny wpis – DZIĘKI!
Rzeczywiście źle użyto tego sformułowania. W powiedzeniu tym wyjątek potwierdza ISTNIENIE reguły – w tym sensie, że gdyby nie istniały wyjątki, nie byłoby potrzeby formułowania reguły.
Witam,
Rozumiem że Państwo podchodzicie luźno do tematu i macie już jakąś wiedzę. Ja natomiast dopiero założyłem prostą stroną składającą się z bootstrapa: html + css + js i za cholerę nie wiem gdzie mam dodać tą linijkę: „Content-Security-Policy: default-src 'self’ cdn.example.com; img-src img.example.com;” jeśli nie polecacie tego robić w metatagu..
Ja próbuję dodać tą linijkę w .htaccess: „Header set Content-Security-Policy „default-src 'self’ (..)” ale strona https://observatory.mozilla.org/
Pokazuje mi że nie używam CSP, tym bardziej że chcę skorzystać z tego 2.0
Gdzie dodaje się dokładnie CSP?
pozdr
Po co używacie skryptu cgi do tworzenia raportu i go nie udostępniacie? tylko w błąd wprowadzacie użytkownika bo on może myśleć że w cgi będzie zapisany json’owy raport a to nie prawda. Wskazujecie w nagłówku scieżkę do skryptu który wygeneruje raport.. DUŻY babol Panowie, żeby nie powiedzieć porażka! Wystarczyłaby tylko informacja o tym dlaczego jest akurat .cgi