CSP, XSS, Clickjacking czyli co jest czym?

Na początek chciałbym rozjaśnić tematykę. Zakładam, że choć każdy zna (powinien przynajmniej) XSS, nie każdy musi znać CSP (choć powinien). Krótko mówiąc, rzecz rozchodzi się o bezpieczeństwo naszych serwisów internetowych.

Trochę teorii:

  1. CSP (Content-Security-Policy) – mechanizm pozwalający w jasny sposób określić pochodzenie zasobów, z których korzysta strona WWW. W zamyśle twórców (Adam Barth, Mike West z Google Inc. oraz Dan Veditz z Mozilla Foundation) miał chronić przed atakami XSS, co było bolączką tamtych czasów – i jest niestety do dzisiaj. Pozwala on poinformować przeglądarkę o tym na co pozwalamy, ale przede wszystkim zabezpieczyć przed modyfikacjami o charakterze zabronionym – oczywiście zabronionym przez twórcę serwisu internetowego. Wraz z pozytywnym odbiorem wersji pierwszej mechanizmu – czyli realizacją celu – osiągnięto dużo więcej. Zaproponowane rozwiązanie oprócz eliminacji XSS, pozwoliło zabezpieczyć (choć bezpieczniej będzie napisać – utrudnić) ataki CSS injection, frame injection oraz pośrednio np. Clickjacking. Czyż nie brzmi to obiecująco?
  2. Clickjacking (User Interface redress attack, UI redress attack, UI redressing) to nic innego jak oszustwo, którego celem jest kliknięcie w coś innego niż w rzeczywistości widzimy. Zasada jest prosta – przysłaniany jest obiekt, którego spodziewa się użytkownik – obiekt klikalny – poprzez obiekt, który prowadzi do zupełnie innego zasobu, często prowadzącego do mniejszych lub większych szkód. Obiekt szkodliwy jest przezroczysty, co nie wzbudza niepokoju u użytkowników. Clickjacking może istnieć w celowo spreparowanej stronie lub być powiązany z atakiem XSS, o którym za chwilę.
  3. CSS injection – mało rozpowszechniony typ ataku, jednak w swoim rozumieniu bardzo intuicyjny. Polega na wstrzyknięciu zewnętrznego arkusza stylów. Prowadzi to do podmiany arkuszy, nadpisania ich (efekt wizualny), wykonania zewnętrznego skryptu (parametr url w stylach) oraz wstrzyknięcia skryptu JS.
  4. Frame injection często występuje w parze z Clickjacking. Zasada jw. – jednak podmiotem, którego dotyczy jest ramka.
  5. XSS (Cross-site scripting) to nic innego, jak osadzanie na atakowanej stronie skryptu (najczęściej jest to JavaScript, rzadziej VBScript i inne), który wykonuje złośliwe zadania. Do zagrożeń powiązanych z tym atakiem zaliczamy: przechwyt ciasteczek (w tym sesyjnych!), uruchomienie keyloggera w przeglądarce ofiary, wstrzyknięcie – o zgrozo – innej złośliwej web-aplikacji poprzez ramkę, a także dynamiczną podmianę dowolnej treści na stronie/stronach. O tyle powyższe zagrożenia są niebezpieczne, że odczytane wrażliwe dane mogą zostać przesłane na dowolny adres, wykorzystując asynchroniczną komunikację sieciową. Cóż, może te kilka zdań nie obrazują właściwego zagrożenia, ale XSS wciąż, pomimo swojego stażu, jest jednym z najgroźniejszych problemów współczesnych aplikacji webowych.

Z informacji podawanych przez magazyn Programista (33) szacunkowo aż 60% – 80% aplikacji funkcjonujących w 2014 roku w Internecie było podatnych na ataki XSS.

Wyniki badań Ariela Sancheza z IO Active Labs, dotyczące mobilnych aplikacji bankowych pokazują podatność na XSS na poziomie 50%. Co tutaj jest najciekawsze – w tym przypadku akurat najgorsze jest to, że we wnioskach znalazły się dużo gorsze problemy badanych aplikacji w dużo szerszej skali.

Natomiast dzięki WPScan Vulnerability Database (serwisowi badającemu bezpieczeństwo WordPressa) i ich statystykom około 40% luk bezpieczeństwa w WP stanowią luki typu XSS i nie byłoby w tym nic dziwnego, gdyby dotyczyły one niesprawdzonych często wtyczek, ale w dużej mierze związane są z samym silnikiem WP. Chociaż na uwagę zasługuje fakt, że jest coraz lepiej. O samych podatnościach WordPressa można na bieżąco poczytać na stronie serwisu. Jeżeli natomiast chcecie być na bieżąco w temacie bezpieczeństwa systemu WP zachęcam do odwiedzenia strony WPZEN.

Przeprowadzony kilka lat temu raport CERT dotyczący bezpieczeństwa stron rządowych był łagodnie rzecz ujmując mało optymistyczny. Audyt 15 serwisów z 8 instytucji państwowych wykrył 167 błędów z czego 2/3 to błędy typu XSS. Parę lat minęło i intuicja mi podpowiada, że w chwili obecnej nie jest wcale dużo lepiej, gdyż trwa wyścig – powstają nowe metody ataków, a tematyka zabezpieczenia serwisów często zaniechana przez niekompetencje programistów, którzy często są usprawiedliwiani niekompetencjami przełożonych lub ignorowanie przez nich tematu zabezpieczeń serwisu.

 

Polityka bezpieczeństwa w przeglądarce

Jak już wspomniałem we wstępie CSP polega na informowaniu o poziomie zabezpieczeń w dostępie do zasobów, z których korzysta aplikacja. Mimo tak prostego wyjaśnienia CSP ma olbrzymi zakres zastosowania, a przez to że ciągle jest rozwijana, zakres ten będzie coraz większy. Rzecz rozbija się o definicję w nagłówku odpowiedzi HTTP (z serwera) o nazwie Content-Security-Policy. Alternatywna definicja może zostać przekazana w metadanych strony.

Definicja w metadanych stanowi alternatywę dla podstawowego sposobu przekazywania informacji. Związane jest to z tym, że dane <meta> można w łatwy sposób zmienić po stronie klienta.

Wystarczy, że atak XSS będzie zakładał istnienie CSP zawarte w metadanych, wówczas właściwy skrypt wystarczy iż będzie poprzedzony zmianą dyrektyw, która zmieni poziom zabezpieczenia (alternatywnie usunie znacznik <meta>) i atak przebiegnie, jakby CSP w ogóle nie istniał.

Mechanizm z wykorzystaniem znacznika, dla zachowania większego bezpieczeństwa, nie weryfikuje wszystkich dyrektyw (patrz report-uri).

Pomyślicie zapewne dlaczego możliwość przekazywania poziomu zabezpieczenia w metadanych? Odpowiem, że mądrzy ludzie to wymyślili i mieli jakiś powód.

Sytuację, której doświadczyłem, a w której swoją rolę odgrywały metadane CSP było tworzenie aplikacji mobilnej z wykorzystaniem Apache Cordova. Tam mamy do czynienia z plikiem źródła HTML (najczęściej index.html), w którym umieszczamy kod HTML. Ten kod wraz z arkuszami stylów stanowi warstwę prezentacji i uruchamianie tej warstwy następuje lokalnie na urządzeniu użytkownika. W tym przypadku umieszczenie definicji w nagłówku w ogóle nie wchodzi w grę.

Jest to jedyny powód, którego doświadczyłem, a zarazem bardzo silny argument. Intuicja mi jednak podpowiada, że powodów takiego standardu było więcej.

 

Wsparcie przeglądarek

Wdrożenie CSP wymaga współpracy podmiotu standaryzującego metodę zabezpieczeń i twórców przeglądarek. O ile możemy być zadowoleni z efektów prac pomysłodawców i osób odpowiedzialnych za rozwój o tyle trzeba się liczyć z ograniczeniami w przypadku obsługi CSP przez przeglądarki.

Poniżej diagramy obrazujące wsparcie standardu CSP w wersja 1.0 i 2.0 oraz wnioski do nich.

csp1

Wsparcie CSP 1.0

 

csp2

Wsparcie CSP 2.0

 

W przypadku CSP 1.0 można być trochę zawiedzionym, jeśli chodzi o Microsoft, ale może przemilczmy ten temat. Przyjmujemy tutaj, że wsparcie CSP w przeglądarkach Microsoftu rozpoczyna się od Edge.

Wersja 2.0, która powinna być obsługiwana w chwili obecnej przez wszystkie przeglądarki, aby rozwój można było kontynuować kompleksowo, może poszczycić się obsługą Google Chrome i pochodnych oraz niemal w pełni przez Mozillę Firefox. Problem z wsparciem mają IE (tutaj już chyba wsparcie nigdy nie powstanie), Edge (patrząc na to, że w końcu ludzie z MS wsparli wersję 1.0, za jakiś czas, pewnie nieprędko, będzie podobnie z wersją 2.0 – żyjmy w nadziei), Safari i wersje mobilne wszystkich wymienionych przeglądarek.

Wniosek nasuwa się jeden – jeśli chcemy zwiększyć bezpieczeństwo podczas przeglądania Internetu korzystajmy z produktu Mozilli i Google’a.

 

Od słów do czynów

Przejdźmy zatem do omówienia kolejno słów kluczowych związanych z tematem. Na CSP składają się dyrektywylisty źródeł (dozwolonych dla zasobów, do których się odnoszą.

Content-Security-Policy: <dyrektywa1> <źródło1> <źródło2> <źródło...>;
[<dyrektywa2> <źródło1> <źródło2> <źródło...>;] ...

Wszystko wygląda prosto i przejrzyście i zapewniam Was, że jak tylko poznacie dyrektywy i oznaczenia źródeł nadal tak pozostanie. Zanim przejdę do omawiania kluczy przykład określania CSP w nagłówku odpowiedzi w języku PHP:

header("Content-Security-Policy: default-src 'self'");

oraz NodeJS:

request.setHeader("Content-Security-Policy", "default-src 'self'");

 

Dyrektywy:

  • base-uri
  • child-src
  • connect-src
  • default-src
  • font-src
  • form-action
  • frame-ancestors
  • frame-src
  • img-src
  • media-src
  • object-src
  • plugin-types
  • report-uri
  • sandbox
  • script-src
  • style-src

 

Prześledźmy szczegółowo wszystkie. Dyrektywa base-uri (CSP 2.0) ogranicza przed szkodliwą manipulacją tagiem <base> przez atakującego. Tag ten pozwala określić lokalizację bazową dla linków względnych i domyślny atrybut target.

Kolejny child-src jest odnowioną dyrektywą frame-src (rozszerza ją) przez co dotyczy wyłącznie wersji 2.0. Dyrektywa, z której się wywodzi od wersji 2.0 jest w stanie deprecated. Związana jest z zagnieżdżonymi kontekstami przeglądarki – ramki oraz web workerzy HTML5. Każde źródło zagnieżdżonego kontekstu podlega weryfikacji tej dyrektywy. Jeśli będzie niedozwolone przeglądarka zablokuje taki element.

Następny connect-src (od wersji 1.0) precyzuje dostęp do źródeł ładowanych z poziomu skryptów. Dotyczy żądań asynchronicznych XMLHttpRequest, konstruktorów WebSocket, EventSource, a także żądań w technologii Beacon.

Dyrektywa default-src to nic innego jak określenie domyślnych źródeł dla dyrektyw niezdefiniowanych. Podczas tworzenia aplikacji WWW zalecane jest rozpoczęcie od dyrektywy default-src ‚self’, a z czasem w miarę potrzeb doprecyzowanie konkretnych dyrektyw do poziomu mniej restrykcyjnego.

Doczepianie domyślnej dyrektywy do nagłówków żądań można określić w pliku konfiguracyjnym naszego kontenera webowego. Dla Apache będzie to:

Header set Content-Security-Policy "default-src 'self';"

natomiast dla nginx:

add_header Content-Security-Policy "default-src 'self';";

Należy pamiętać, że nie wszystkie dyrektywy uwzględniają wartość domyślną. Lista dyrektyw uwzględniających default-src to: child-src, connect-src, font-src, frame-src, img-src, media-src, object-src, script-src, style-src.

Lecimy dalej – font-src – działa od wersji 1.0 polityki i dotyczy, jak się można domyślić źródeł czcionek.

Akcje formularzy możemy ograniczyć poprzez listę źródeł dyrektywy form-action, która została dodana w CSP 2.0.

Mimo zalet dotychczas wymienionych dyrektyw, na wielką uwagę zasługuje frame-ancestors (2.0). Ww. stanowią zabezpieczenie przed tym, co znajdzie się na naszej stronie. Tutaj mamy do czynienia z mechanizmem blokady kontrolującej umieszczanie naszej strony (elastyczniej adresu) w zewnętrznych serwisach poprzez ramki, elementy <object>, <applet> itd. Tutaj należą się brawa twórcom, którzy ulepszyli problemowy nagłówek X-Frame-Options, nadpisywany aktualnie przez omawianą dyrektywę CSP. Nie da się ukryć, że jest to bardzo konkretny środek zapobiegający atakom Clickjacking.

Poniżej lista możliwości:

  • blokada zagnieżdżania – ‚none’
  • zagnieżdżanie z zasadą SOP – ‚self’
  • zagnieżdżanie na określonym hoście – host

Przechodzimy teraz do frame-src, które choć jest aktualnie w wersji deprecated to pamiętajmy, że od wersji 36 Mozilla Firefox nie radzi sobie jeszcze z jej rozszerzeniem child-src i warto (przynajmniej na razie) uwzględniać jeszcze tę dyrektywę w określaniu ram polityki w tworzonych serwisach. Prognoza mówi jednak, że w CSP 3.0 nie będzie miejsca na frame-src. Biorąc pod uwagę, że od Firefox 36 minęło już trochę czasu można domyślić się, że Mozilla pracuje już nad pełnym wsparciem CSP 3.0, a niżeli martwi się o wsparcie do wersji 2.0. Taki mały syndrom Microsoftu 🙂

Jesteśmy w połowie – teraz img-src (1.0). Dotyczy źródeł, z których możemy ładować obrazki. Szeroki zakres od CSS po atrybut poster węzła <video>.

W przypadku media-src (1.0) kontekst dyrektywy dotyczy multimediów, natomiast dla object-src (1.0) kontekst migruje na obiekty obsługiwane przez wtyczki przeglądarki (rodzaje dozwolonych wtyczek precyzujemy w dyrektywie plugin-types – pamiętając o ograniczeniu Firefoxa). Tutaj nie będę się rozpisywał, zachęcam do lektury dokumentacji CSP osoby zainteresowane lub do pytań w komentarzach.

Czas na ciekawą report-uri. Raportowanie bardzo przydaje się do kontrolowania polityki i weryfikacji poprawności, celem wyszukania luk w zabezpieczeniach, bądź identyfikacji czynu (strony, kontekstu) zabronionego. Źródło dyrektywy to tak naprawdę adres na który zostają wysyłane raporty metodą POST po naruszeniu ograniczeń dostępu. Jest to pomocne zarówno w trakcie rozwoju systemu (możliwość szybkiego znalezienia błędnej polityki, być może wyłącznie na pojedynczych stronach serwisu), jak i wdrażaniu polityki CSP w już istniejący mniejszy lub większy serwis – wówczas jest to nieocenione źródło informacji.

Dodatkowo pomocna jest możliwość przełączenia całej polityki CSP w tryb wyłącznie raportowania. Wystarczy nazwę pola w nagłówku zmienić z Content-Security-Policy na Content-Security-Policy-Report-Only. Taka zmiana spowoduje spływanie raportów bez blokowania źródeł przez przeglądarkę. Wówczas interpretując raporty możemy stopniowo rozluźniać restrykcje naszej polityki, by zapewnić bezpieczeństwo przy kompletnie funkcjonalnym serwisie.

Przejdźmy do trybu piaskownicy HTML5. Jest on wspierany dyrektywą sandbox. Zasób w tym trybie posiada pewne ograniczenia:

  • wyłączone wysyłanie formularzy (źródło allow-forms zdejmuje ograniczenie)
  • ścisła zasada SOP (powiązane allow-same-origin)
  • wyłączone wykonywanie skryptów (powiązane allow-scripts)
  • zabroniona zmiana nawigacji (powiązane allow-top-navigation)
  • wyłączone pop-upy (powiązane allow-popups)
  • wyłączony interfejs blokowania wskaźnika myszy (powiązane allow-pointer-lock)

Pewnie myślicie, po co tryb sandbox? Ha, bywają sytuacje, kiedy może się przydać, np.:

  • wyświetlanie wiadomości e-mail w formacie HTML
  • wyświetlanie załączników HTML

Ma to sens 🙂

Zostały nam dwie dyrektywy – script-srcstyle-src (obie od wersji 1.0). Również mają podobne działanie, z tym, że pierwsza dotyczy skryptów (w dowolnym języku wspieranym przez przeglądarkę, a druga, arkuszy stylów. Możliwe źródła:

  • host (bądź pełna ścieżka od wersji 2.0)
  • ‚none’ (blokowanie)
  • ‚self’ (pozwolenie z tego samego źródła)
  • ‚unsafe-inline’ (pozwolenie na skrypty/style zdefiniowane wewnątrz kodu)
  • ‚unsafe-eval’ (dotyczy wyłącznie skryptów – pozwala na wywołanie funkcji eval()setTimeout()setInterval() oraz konstruktora funkcji Function)

Bardzo zachęcam jednak do niestosowania ani źródła ‚unsafe-inline’, ani ‚unsafe-eval’, gdyż bardzo obniżamy w ten sposób poziom zabezpieczenia przed atakami XSS.

Jeśli jednak nie masz wyjścia (bo np. framework, z którego korzystasz wstrzykuje skrypty typu inline, bądź też takie style) konieczne kilka razy przeczytaj najbliższy akapit.

Mechanizm Nonce Inline Hash, jako złoty środek na zabezpieczenie skryptów i stylów typu inline. Brzmi bardzo obiecująco. Już tłumaczę o co chodzi. Nonce umożliwia dopuszczenie skryptu inline do działania. Muszą jednak zostać spełnione dwa warunki:

  • do dyrektywy musi zostać dołączone źródło ‚nonce-{random}’, gdzie {random} to silnie pseudolosowy token
  • <script>, bądź <style> musi zawierać atrybut nonce z wartością {random} podaną w źródle dyrektywy polityki CSP

Atrybut nonce można dodawać również do skryptów dodanych przez src, co w pewnych przypadkach pozwoli uzyskać spójną politykę CSP, analogicznie, bądź łatwiej niż we wcześniej opisanych sytuacjach. Mechanizm Inline Hash z kolei wykorzystuje jednokierunkowe funkcje skrótu (sha256, sha384 lub sha512). Tutaj również muszą zostać spełnione dwa warunki, by skrypt lub styl został włączony w renderowanie:

  • do dyrektywy musi zostać dołączone źródło ‚shaYYY-{base64hash}’, gdzie {base64hash} to hash typu sha256, sha384 lub sha512 (w zależności od YYY) zawartości znaczników, których dotyczy, czyli <script> lub <style>
  • hash musi być zakodowany algorytmem base64

Przykładowe generowanie funkcji skrótu – należy zwrócić uwagę również na białe znaki, które na repozytorium serwisu niepostrzeżenie mogą ulegać zmianie (np. przez problemy z formatowaniem kodu przez niektórych członków zespołu programistycznego):

echo -n "alert('Test CSP');"
| openssl dgst -sha512 -binary 
| openssl enc -base64 -A > sha512.log

 

W przypadku nonce token musi być każdorazowo inny (podobnie jak w zabezpieczeniach Cross-Site-Request-Forgery). Znacząco to utrudni działanie potencjalnemu atakującemu. W przeciwnym razie będzie mógł łatwo przewidzieć wartość tokenów.

 

Źródła

Niektóre źródła już zostały wyjaśnione wraz z opisem dyrektyw. Teraz postaram się przedstawić małą syntezę i uzupełnić o niezbędne informacje.

  • ‚self’ – chyba jedno z najczęściej wymienianych źródeł, dotyczy pozwolenia do tego samego hosta zgodnie z zasadą SOP
  • host
    • bez protokołu np. domain.com
    • z protokołem np. https://domain.com
    • z portem np. domain.com:8888
    • ze znakiem wyrażenia regularnego * – gwizdką (gwiazdka w domenie musi następować zaraz po protokole lub w dowolnym miejscu na pozycji portu) np. http://*.domain.com:*
  • ścieżka do pliku (od wersji 2.0)
    • bezpośredni adres do pliku
    • adres do katalogu zakończony slashem zezwalający na dostęp do plików z katalogu
  • ‚none’ – zabrania wszystkim zasobom danej dyrektywy
  • ‚unsafe-inline’
  • ‚unsafe-eval
  • ‚nonce-YYYY’ (od wersji 2.0)
  • ‚shaYYY-ZZZZ’ (od wersji 2.0)
  • inne dedykowane konkretnym dyrektywom

 

Podsumowanie

Artykuł miał na celu zobrazować, jaki mechanizm w chwili obecnej staje się podstawowym w walce przede wszystkim z atakami XSS. To tylko pokazuje, jak trudnym problemem jest XSS. Czas mija, a nadal nie ma skutecznego mechanizmu obrony i zapewne nie będzie, bowiem intruzi również idą z duchem czasu i powstają albo coraz to nowe zagrożenia, albo nowe sposoby ataków w tym samym zakresie.

Polityka CSP niebawem okaże się niezbędna podczas wdrażania serwisów internetowych, co jest uzasadnione. Ale z drugiej strony głupio byłoby zignorować rozwojowy mechanizm, na poczet którego twórcy przeglądarek poświęcili mnóstwo czasu.

Pamiętajcie jednak, że polityka CSP nie ma być zamiast innych zabezpieczeń, ale obok innych zabezpieczeń. Musi stanowić dodatkową warstwę obrony, a nie być wyłącznie alternatywą. Nigdy bowiem nie mamy pewności z jakiej przeglądarki, bądź jakiej wersji przeglądarki korzysta użytkownik, a to od tego zależy czy ta dodatkowa warstwa obrony/ochrony użytkownika zostanie włączona i poprawnie obsłużona.

 

 

Dziękuję za uwagę. Już mogę zapowiedzieć kolejny artykuł, w którym opiszę kilka nowinek, które mogą ujrzeć światło dzienne w wersji CSP 3.0.

Tymczasem odsyłam do artykułu z przykładami użycia opisanych wyżej dyrektyw: CSP – przykłady. Poza tym polecam artykuł dotyczący trybu raportowania w CSP.

Pozdrawiam!