Jak działa ta kontrola
Scanaris czyta nagłówki Set-Cookie, które Twój serwer wysyła przy ładowaniu strony głównej, i sprawdza atrybuty każdego ciasteczka. Weryfikuje trzy rzeczy: czy ma Secure (wymagane tylko, gdy strona działa po HTTPS), czy ma HttpOnly i czy deklaruje SameSite. Jeśli nazwa ciasteczka sugeruje sesję lub uwierzytelnienie (session, auth, token, sid i podobne), znalezisko ma średnią wagę; w przeciwnym razie niską. Jest jeden szczególny przypadek z własną średnią wagą: SameSite=None bez Secure, które nowoczesne przeglądarki po prostu odrzucają, więc ciasteczko nie działa, a dodatkowo zdradza konfigurację pomyślaną pod użycie między stronami. Widoczne są tylko ciasteczka wysłane w tej pierwszej odpowiedzi; te tworzone po zalogowaniu lepiej sprawdzić poleceniem poniżej albo skanem uwierzytelnionym.
Co oznacza każdy atrybut
- Secure: ciasteczko podróżuje tylko po HTTPS. Bez niego żądanie HTTP (stary link, publiczna sieć) wysyła je jawnie i każdy w sieci je odczyta.
- HttpOnly: JavaScript nie może go odczytać. To różnica między XSS, który kradnie sesję, a takim, który nie może.
- SameSite: kiedy jest wysyłane z innej strony.
Laxwysyła je przy zwykłej nawigacji, ale nie przy żądaniach inicjowanych przez inną witrynę;Strictnigdy z zewnątrz;Nonezawsze, i wymagaSecure. To główna obrona przed CSRF.
Co atakujący robi z ciasteczkiem sesji bez tych atrybutów
Bez HttpOnly każdy wstrzyknięty skrypt czyta document.cookie, wysyła wartość na swój serwer i loguje się Twoją sesją. Bez SameSite obca strona może wysłać formularz do Twojej witryny z dołączonym ciasteczkiem i złożyć zamówienie albo zmienić e-mail w imieniu użytkownika. Bez Secure wystarczy, że użytkownik otworzy link http:// do Twojej domeny w sieci współdzielonej. We wszystkich trzech przypadkach wynik jest ten sam: ktoś działa jako Twój użytkownik.
Jak to naprawić w zależności od frameworka
// Express z express-session
app.use(session({
cookie: { secure: true, httpOnly: true, sameSite: 'lax' }
}));
// Next.js (Route Handler lub Server Action)
cookies().set('session', value, { httpOnly: true, secure: true, sameSite: 'lax', path: '/' });
// Nuxt (server/api)
setCookie(event, 'session', value, { httpOnly: true, secure: true, sameSite: 'lax', path: '/' });
Z Supabase w aplikacji z serwerem biblioteka @supabase/ssr trzyma sesję w ciasteczkach z tymi atrybutami; w czystej SPA sesja trafia do localStorage, co mierzy kontrola tokeny w storage. W Django, Rails czy Laravelu atrybuty konfiguruje się w ustawieniach sesji i domyślnie są rozsądne; sprawdź, czy nikt ich nie obniżył, żeby „naprawić" problem z developmentu.
Sprawdź, czy naprawione
curl -sI https://twojadomena.com | grep -i "set-cookie"
curl -sI -X POST https://twojadomena.com/api/login -d "..." | grep -i "set-cookie"
Każde ciasteczko sesji powinno pokazywać Secure; HttpOnly; SameSite=Lax (lub Strict). Jeśli logowanie to formularz, obejrzyj odpowiedź w narzędziach przeglądarki, zakładka Sieć, nagłówki odpowiedzi.
Często zadawane pytania
Moje ciasteczko tworzy JavaScript (analityka, preferencje). Nie może być HttpOnly.
Zgadza się, i nic się nie dzieje: te ciasteczka nie dają do niczego dostępu. Kontrola oznacza je niską wagą za brakujący atrybut; liczy się, żeby te sesyjne go miały.
Potrzebuję SameSite=None, bo moja aplikacja działa w iframe na innej domenie.
Wtedy to uzasadnione, zawsze z Secure. Upewnij się, że pozostałe obrony przed CSRF (tokeny w formularzach, sprawdzanie Origin) są aktywne, bo SameSite już Cię nie chroni.
Lax czy Strict?
Lax dla większości: utrzymuje sesję, gdy użytkownik przychodzi z zewnętrznego linku. Strict, jeśli ciasteczko daje dostęp do czegoś bardzo wrażliwego i akceptujesz, że użytkownik musi zalogować się ponownie, przychodząc z zewnątrz.