Flaga cookies SameSite – jak działa i przed czym zapewnia ochronę?

18 marca 2019, 08:50 | Teksty | komentarzy 6
: zin o bezpieczeństwie - pobierz w pdf/epub/mobi.

Rozwiązania z dziedziny bezpieczeństwa starają mitygować zagrożenia, które pojawiają się w świecie aplikacji internetowych. Jednym z przykładów takich działań jest możliwość dodania do ciasteczek HTTP flagi SameSite. Sprawdźmy, jak działa SameSite oraz przed jakimi niebezpieczeństwami może nas ochronić.

Podstawy – przeglądarki, ciasteczka i sesja

Protokół HTTP jest bezstanowy (ang. stateless). Oznacza to, że bez zastosowania dodatkowych mechanizmów nie jest możliwe ustanowienie sesji pomiędzy klientem HTTP, najczęściej przeglądarką WWW, a serwerem HTTP.

Zastosowanie protokołu HTTP do czegoś więcej, niż proste pobieranie informacji z sieci, poskutkowało stworzeniem mechanizmu zarządzania sesją. W tym celu powstały tzw. ciasteczka. Serwer, wysyłając nagłówek Set-Cookie, może poinstruować przeglądarkę, by ta zapisała w pamięci ciasteczko o określonej nazwie i wartości. Przeglądarka z kolei automatycznie dołączy ciasteczko do zapytania wykonywanego do określonej domeny.

Ważny jest tutaj fakt, że przeglądarka wyśle ciasteczka do właściwego serwera niezależnie od tego, co lub kto wyzwolił zapytanie. Ciasteczka zostaną również dołączone do zapytania, jeżeli jego źródłem była inna domena, niż ta, do której wysyłane jest zapytanie. Możemy to w prosty sposób zweryfikować. W pierwszym kroku musimy uruchomić DevTools (CTRL/CMD + Alt + I). Następnie wystarczy, że klikniemy w przycisk zamieszczony poniżej. Wykonanie tej operacji spowoduje, że do ramki iframe załadowana zostanie zawartość strony https://www.google.com.

Przechodząc do zakładki „Network”, możemy zauważyć, że na jednej z ostatnich pozycji na liście powinno pojawić się odwołanie do google.com. Analizując zawartość sekcji „Headers”, zauważymy, że pośród wysłanych przez przeglądarkę danych znalazł się nagłówek „Cookie”. Znajdziemy w nim ciasteczka, jakie przeglądarka zapisała wcześniej dla domeny google.com (Rysunek nr 1).

Rysunek 1.

Przy pomocy fragmentu kodu uruchomionego w kontekście domeny sekurak.pl zmusiliśmy przeglądarkę do tego, by wysłała zapytanie do innej domeny, a równocześnie dołączyła w żądaniu poprawne ciasteczka. Przesłane dane mogą służyć aplikacji uruchomionej pod adresem google.com np. do określenia, czy użytkownik jest aktualnie uwierzytelniony (posiada aktywną sesję), a co za tym idzie, również do autoryzacji dowolnej operacji.

Nadużycie

Przeprowadzony eksperyment potwierdził, że przeglądarki WWW nie rozróżniają przypadków, w których to użytkownik wyzwolił żądanie do określonego zasobu od tych, kiedy akcja została wygenerowana przez skrypt lub inny fragment kodu. Nic nie stoi przecież na przeszkodzie, by strona google.com została załadowana w ramce automatycznie, a nie dopiero po kliknięciu w przycisk. Niezależnie od tego, co lub kto wyzwolił zapytanie, przeglądarka dołączy do generowanego zapytania ciasteczka, które odpowiadają domenie, dla jakiej zostały utworzone.

Właśnie takie działanie przeglądarek WWW umożliwia przeprowadzanie ataków Cross-Site Request Forgery (CSRF) czy Pixel Perfect. Można powiedzieć, że chodzi tutaj o przypadki ataków, które wymuszają, aby ciasteczka zapisane w przeglądarce zostały wysłane w zapytaniu do domeny z podatną/atakowaną aplikacją.

SameSite na ratunek

Rozwiązaniem problemu nadużywania zachowania przeglądarek względem ciasteczek ma być ustawienie flagi SameSite. SameSite może przyjmować dwie wartości: Lax lub Strict. Zajmijmy się w pierwszej kolejności wartością Strict.

Przypisanie do flagi SameSite wartości Strict spowoduje, że w przypadku gdy zapytanie do serwera zostanie wygenerowane z innej domeny (zapytanie typu cross-site) niż ta, dla której ciasteczko zostało utworzone, przeglądarka nie dołączy takiego ciasteczka do żądania.

Sprawdźmy to. W tym celu musimy przygotować odpowiednie środowisko testowe. W pierwszej kolejności przejdźmy do skryptu, do którego prowadzi link zamieszczony poniżej. Odnośnik przekieruje nas do domeny training.securitum.com, gdzie umieszczony jest skrypt setcookie.php. Jego zadaniem jest przesłanie w odpowiedzi HTTP odpowiedniego nagłówka, który ustawi dla domeny training.securitum.com ciasteczko o nazwie “test”.

http://training.securitum.com/samesite/setcookie.php

To czy ciasteczko ustawiło się poprawnie, możemy zweryfikować przy pomocy drugiego skryptu checkcookie.php.

http://training.securitum.com/samesite/checkcookie.php

Alternatywnie możemy skorzystać z DevTools (CTRL/CMD + Alt + I).

Rysunek 2.

Po kliknięciu w link do skryptu checkcookie.php powinniśmy zauważyć komunikat z informacją, która sugeruje, że ciasteczko zostało przesłane (Rysunek nr 2). Na razie przeglądarka zachowała się w standardowy sposób.

Przyszła pora na zmodyfikowanie ciasteczka i ustawienie dla niego flagi SameSite z wartością Strict. W tym celu uruchamiamy skrypt setcookie.php jeszcze raz, tym razem z innymi parametrami.

http://training.securitum.com/samesite/setcookie.php?samesite=strict

To, czy flaga ustawiła się poprawnie możemy zweryfikować przechodząc do zakładki „Application”, a następnie „Cookies”. Lista ciasteczek powinna zawierać to, które sami dodaliśmy, tym razem z flagą SameSite ustawioną na Strict (Rysunek nr 3).

Rysunek 3.

Jeżeli wartość flagi SameSite została ustawiona poprawnie, możemy kliknąć w link prowadzący do skryptu checkcookie.php jeszcze raz.

http://training.securitum.com/samesite/checkcookie.php

Tym razem możemy zaobserwować, że testowe ciasteczko nie zostało wysłane. Skrypt checkcookie.php wyświetlił komunikat o braku przesłanego ciasteczka (Rysunek nr 4).

Rysunek 4.

Wygląda więc na to, że flaga SameSite zadziałała prawidłowo. Zapytanie, które zostało wygenerowane z innej domeny (cross-site), nie skutkowało dołączeniem przez przeglądarkę ciasteczek ustawionych dla domeny training.securitum.com.

Lax – nawigacja “top-level” oraz bezpieczne metody HTTP

Politykę Strict można scharakteryzować jako bezwzględną. Jej  wdrożenie może być uznane w niektórych przypadkach za zbyt inwazyjne. Prawdopodobnie z tego powodu SameSite pozwala zastosować jeszcze jedną politykę, którą definiuje wartość Lax. Czym Strict różni się od Lax?

W przypadku polityki Strict kwestią decydującą o tym, czy ciasteczko zostanie wysłane czy nie, jest pochodzenie zapytania. Może być ono typu cross-site – wtedy ciasteczko nie zostanie wysłane, lub same-site – w takim przypadku ciasteczko zostanie dołączone. Algorytm podejmowania decyzji dla polityki Lax został rozszerzony. Jeżeli wygenerowane zapytanie będzie skutkowało top-level navigation, oraz zostanie przesłane z użyciem tzw. bezpiecznej metody HTTP, wtedy ciasteczko zostanie wysłane. Jeżeli użyta została metoda spoza listy bezpiecznych, lub zapytanie nie będzie skutkowało top-level navigation (brak zmiany domeny w pasku adresu), wtedy przeglądarka ciasteczka nie dołączy.

Powstają tutaj co najmniej dwa pytania. Pierwsze z nich dotyczy tego, dlaczego tak ważna jest zmiana adresu w pasku przeglądarki (top-level navigation). Drugie to wyjaśnienie tego co kryje się pod pojęciem bezpiecznych metod HTTP.

Jeżeli zagnieździmy w kodzie strony ramkę iframe lub tag img, a atrybuty src tych elementów będą prowadzić na wskazany przez atakującego adres, użytkownik może nawet nie zauważyć, że przeglądarka wykonała w jego imieniu zapytanie do innej domeny, niż ta w ramach której aktualnie pracuje. Autorzy specyfikacji SameSite przyjęli więc, że adres URL wyświetlany w pasku adresu jest jedynym źródłem informacji, które użytkownik może wykorzystać do ustalenia w kontekście jakiej domeny wykonuje operacje. Dlatego też zmiana tego adresu (top-level navigation) jest konieczna do tego, by ciasteczko chronione przez politykę Lax mogło być przesłane do właściwej domeny.

Według RFC7231 do grona bezpiecznych metod zalicza się: GET, HEAD, TRACE oraz OPTIONS. Określenie „bezpieczne” metody należy odpowiednio interpretować. Nieprzypadkowo słowo safe w dokumencie RFC użyte w tym kontekście pojawia się w cudzysłowiu. Powiązane jest to z ogólnym zaleceniem, aby akcje zmieniające stan aplikacji były wyzwalane tylko z wykorzystaniem takich metod jak POST, PUT czy PATCH, a nigdy przy pomocy metod takich jak chociażby GET, która znajduje się na liście metod bezpiecznych. Najprościej jest to chyba wytłumaczyć tym, że akcje zmieniające stan aplikacji (np. dodanie/usunięcie użytkownika z bazy) nigdy nie powinny być wyzwalane z zastosowaniem metody GET. Metoda GET może być jednak jak najbardziej wykorzystana do wyzwolenia akcji, która jedynie odczytuje dane (nie zmienia stanu aplikacji), czyli w domyśle jest bezpieczna dla aplikacji. Przykładem może być tutaj wywołanie akcji wyszukiwania informacji w bazie (GET /search?query=cats).

Zastosowanie polityki Lax będzie skutkowało tym, że gdy przeglądarka zostanie zmuszona do wysłania zapytania typu cross-site, ale z wykorzystaniem bezpiecznej metody HTTP (w domyśle takiej, która nie wyzwoli niebezpiecznej akcji w aplikacji), oraz w sposób, który będzie wyzwalał top-level navigation, wtedy ciasteczko zostanie dołączone do zapytania. Polityka Lax w przypadku wysłania zapytania z wykorzystaniem np. metody POST spowoduje jednak, że ciasteczko nie zostanie wysłane – metoda POST nie znajduje się na liście bezpiecznych metod HTTP i po stronie aplikacji może być użyta do wykonania operacji, która zmienia jej stan. Podobny efekt przyniesie wygenerowanie zapytania z wykorzystaniem metody GET, ale z poziomu ramki iframe – wykorzystujemy co prawda bezpieczną metodę HTTP, ale w sposób, który nie powoduje zmiany adresu w pasku przeglądarki – ciasteczko nie powinno zostać dołączone.

Zweryfikujmy działanie polityki Lax na przykładzie. W tym celu musimy przypisać dla ciasteczka flagę SameSite z wartością Lax.

http://training.securitum.com/samesite/setcookie.php?samesite=lax

To, czy kod wykonał się poprawnie możemy ustalić przechodząc ponownie do zakładki „Application”, a następnie sprawdzając zawartość kolumny „SameSite” dla testowego ciasteczka. Powinniśmy tam znaleźć wartość Lax (Rysunek nr 5).

Rysunek 5.

Teraz zweryfikujmy, jak przeglądarka zachowa się po kliknięciu w link „GET http://training.securitum.com/samesite/checkcookie.php”.

GET http://training.securitum.com/samesite/checkcookie.php

Jeżeli wszystko pójdzie zgodnie z planem, skrypt checkcookie.php wyświetli nam komunikat o poprawnym przesłaniu ciasteczka.

Idąc dalej, musimy zweryfikować przypadek z załadowaniem skryptu checkcookie.php w ramach ramki iframe. Stanie się to po kliknięciu w przycisk “iframe GET checkookie.php”.

Mimo, że załadowanie zasobu w ramce powoduje wysłanie zapytania GET, czyli takiego, które znajduje się na liście metod bezpiecznych, skrypt checkcookie.php wyświetlił komunikat o braku ciasteczka. Do tej pory polityka Lax zachowuje się zgodnie z założeniami.

Pozostało jeszcze zweryfikować, jaki będzie efekt wyzwolenia zapytania, które zmienia adres w pasku przeglądarki, ale użyta do tego metoda nie znajduje się na liście bezpiecznych. Posłuży nam do tego przycisk “POST checkcookie.php” (zostaniemy przekierowani z serwisu sekurak.pl na training.securitum.com).

Jeżeli wszystko poszło zgodnie z planem, tym razem również powinniśmy zobaczyć komunikat o braku ciasteczka.

Bilans zysków i strat

SameSite pozwala zabezpieczyć aplikację i jej użytkowników przed zagrożeniami, które wymagają wykonywania zapytań typu cross-site. XSSI, CSRF czy wspomniany wcześniej Pixel Perfect zostaną zablokowane już na etapie wysyłania złośliwego zapytania. SameSite wnosi więc istotny wkład w dziedzinie ochrony przed atakami, których skutkiem może być wyciek danych pomiędzy różnymi domenami.

Jako ciekawostkę można odnotować fakt, że SameSite może chronić nie tylko przed atakami, które wprost kojarzą się z aplikacjami WWW. Flaga SameSite jest wymieniana jako jeden z wektorów ochrony przed atakami Spectre oraz Meltdown.

Wdrożenie SameSite musi być jednak poprzedzone analizą wpływu tego mechanizmu na sposób korzystania z aplikacji. Dodanie flagi SameSite z wartością Lax w przypadku poprawnie zaimplementowanych aplikacji powinno odbyć się bezboleśnie. Zastosowanie polityki Strict może jednak przyprawić użytkowników o zawrót głowy – będzie to związane z koniecznością wprowadzania poświadczeń przy każdym kliknięciu w link, który prowadzi do zasobu umieszczonego w innej domenie.

Jeżeli bardzo zależy nam na możliwości wykorzystania polityki Strict, proponowanym rozwiązaniem jest wprowadzenie zmian w mechanizmie zarządzania sesją. W skrócie sprowadza się ono do zastąpienia ciasteczka sesyjnego dwoma ciasteczkami, z czego pierwsze będzie nadawało uprawnienia w aplikacji tylko do odczytu, a drugie, z ustawioną flagą SameSite na Strict, będzie decydowało o tym, czy użytkownik może wykonać operacje wymagające wyższych uprawnień (zapis/zmiana). W przypadku gdy aplikacja otrzyma zapytanie bez drugiego ciasteczka (oznaczonego jako SameSite), wymusi ona na użytkowniku ponowne uwierzytelnienie, jeżeli ten będzie chciał wykonać operację wymagającą wyższych uprawnień (np. zapis/zmiana).

Lek na całe zło?

W sieci można znaleźć artykuły, które reklamują SameSite jako uniwersalne rozwiązanie na problemy wynikające z CSRF. W mojej opinii należy jednak zachować większą ostrożność.

Rozważmy przypadek aplikacji, która pozwala na wyzwolenie akcji zmieniającej jej stan z użyciem metody GET. Jest to zachowanie niepoprawne, ale nadal spotykane. Jeżeli użytkownik zostanie przekierowany do adresu URL, który wyzwala określoną akcję, a następnie zauważy, że aplikacja nie wyświetla tego, co oczekiwał, może on spróbować przejść pod wskazany adres jeszcze raz, wybierając chociażby przycisk „Odśwież”. W takim przypadku ciasteczko, którego wysłanie zostało zablokowane, tym razem zostanie przez przeglądarkę wysłane do serwera.

Przede wszystkim jednak ciasteczka same-site stanowią warstwę zabezpieczeń, która implementowana jest po stronie klienta (przeglądarka WWW) i wnoszą one wartość do ochrony przed atakami, które są ściśle związane z tą częścią aplikacji WWW. Dobrą praktyką, a właściwie wymogiem jest jednak poleganie na zabezpieczeniach implementowanych po stronie serwera. Mechanizmy wykorzystywane po stronie klienta mogą być traktowane jako dodatkowa warstwa zabezpieczeń, ale nie gwarant bezpieczeństwa. W praktyce oznacza to, że wdrożenie polityki same-site nie powinno być jednoznaczne z zaniechaniem stosowania dotychczasowych technik ochrony przed Cross-Site Request Forgery.

— Marcin Piosek, hakuje w Securitum

Spodobał Ci się wpis? Podziel się nim ze znajomymi:



Komentarze

  1. patryk

    Coś ten pierwszy przykład ze strict nie działa.

    Odpowiedz
    • Marcin Piosek

      Co konkretnie się dzieje?

      Odpowiedz
  2. zero one

    Dobry art. :-)
    Dzięki i pozdrowienia.

    Odpowiedz
  3. Wiki

    Dzięki za skrypt, przydał się do testowania csrf :)

    Odpowiedz
  4. Eryk

    Dzięki za interesujący artykuł, przykłady działają jak powinny na FireFoxie.

    Odpowiedz
  5. Bartek
    Odpowiedz

Odpowiedz