Sesje, ciasteczka i CSRF

Sprawdź, że wylogowanie zabija sesję, ciasteczko ma flagi, a obca strona nie zmieni stanu, podpinając się pod ciasteczko.

Ten wpis jest częścią serii Podstawy testowania bezpieczeństwa. Jeśli pominąłeś poprzedni, przeczytaj Część 3 — Kontrola dostępu, której skaner nie złapie.


Wprowadzenie — logowanie było testowane. Wyjście nie.

Suite loguje się na starcie i nigdy się nie wylogowuje. Ciasteczko sesji żyje tyle, co profil przeglądarki. Formularz na innej stronie może wysłać POST do waszej, a przeglądarka dołoży ciasteczko, jeśli jej na to pozwolicie.

Trzy kontrole. Żadna nie wymaga nowego narzędzia.

Wylogowanie zabija sesję na serwerze

Zaloguj się. Skopiuj ciasteczko sesji. Wyloguj się. Wyślij żądanie ze skopiowanym ciasteczkiem.

curl -s -o /dev/null -w "%{http_code}" \
  -H "Cookie: session=$OLD" \
  https://staging.example.com/api/me

Chcesz 401. Jeśli nadal dostajesz profil użytkownika, wylogowanie wyczyściło tylko przeglądarkę. Kto skopiował ciasteczko, nadal jest w środku.

To samo po zmianie hasła. Stare sesje powinny umrzeć. Jeśli produkt obiecuje „wyloguj wszystkie urządzenia”, to dwie sesje, nie jedna przeglądarka.

Flagi ciasteczka

W DevTools albo w nagłówku Set-Cookie ciasteczko sesji potrzebuje:

  • Secure — nie pójdzie po zwykłym HTTP
  • HttpOnly — skrypt strony go nie odczyta
  • SameSite — Lax albo Strict, żeby POST z obcej strony go nie zabrał
  • ograniczonego czasu życia, nie ciasteczka, które wygasa w 2035

Brak HttpOnly znaczy, że wstrzyknięty skrypt kradnie sesję. Dlatego część 1 traktuje XSS jako coś więcej niż wyskakujące okienko.

Obca strona nie może zmienić stanu

Postaw mały plik HTML, który wysyła POST na wasz origin stagingowy:

<form action="https://staging.example.com/api/email" method="POST">
  <input name="email" value="attacker@example.com" />
</form>
<script>document.forms[0].submit()</script>

Otwórz go, będąc zalogowanym na stagingu. E-mail na koncie nie może się zmienić.

SameSite=Lax blokuje większość takich POST-ów z obcej strony w obecnych przeglądarkach. To nie powód, żeby pominąć test. Ciasteczko SameSite=None, ustawione po to, żeby widget na innej stronie mógł was wołać, jest dokładnie tym, na którym ten formularz pojedzie. Takie endpointy potrzebują drugiej kontroli, której obca strona nie ma: tokenu CSRF albo nagłówka, którego formularz nie ustawi, sprawdzanego na serwerze.

Test, który patrzy tylko na formularz waszego originu, tego nie zobaczy. Wroga strona jest testem.

Podsumowanie

Wylogowanie musi unieważnić sesję, którą skopiowałeś. Ciasteczko nie może być czytelne dla skryptu ani podpinane przez obcy formularz. Obcy formularz udowodnij na stagingu, nie na własnym UI.

Zadanie na ten tydzień: Skopiuj ciasteczko sesji, wyloguj się i zawołaj nim /api/me. Potem przeczytaj Set-Cookie i zapisz każdą brakującą flagę.


Następny wpis w serii: Część 5 — Kontrole bezpieczeństwa, które należą do CI.