Jak działa ta kontrola
Scanaris patrzy na główną odpowiedź Twojej strony i sprawdza dwa warunki naraz. Pierwszy: odpowiedź ustawia co najmniej jedno ciasteczko (Set-Cookie), którego nazwa zaczyna się od sess, sid, auth, token, jwt, connect.sid, phpsessid lub asp.net, czyli ciasteczko, które z dużym prawdopodobieństwem identyfikuje sesję. Drugi: ta sama odpowiedź niesie Cache-Control z dyrektywą public. Tylko gdy oba są spełnione, otwiera ustalenie o średniej wadze z nazwą ciasteczka i wartością nagłówka. Jest celowo restrykcyjna: ciasteczko preferencji z publicznym cache albo ciasteczko sesji bez public nie są oznaczane.
Dlaczego to ważne
Cache-Control: public to jawna instrukcja dla każdego współdzielonego cache (CDN, reverse proxy jak Varnish czy nginx, proxy firmy lub operatora), że tę odpowiedź można zapisać i dostarczać innym użytkownikom. Jeśli ta sama odpowiedź zawiera sesyjne Set-Cookie, cache może zapisać ciasteczko razem z treścią i dać je następnemu odwiedzającemu. Ten odwiedzający wchodzi z sesją poprzedniego, bez hasła, i widzi jego dane. To błąd z tych, które odkrywa się, gdy klient pisze, że „wszedł na konto innej osoby". Zdarzało się to bankom i dużym sklepom, zawsze przez tę samą kombinację: spersonalizowana treść oznaczona jako cache'owalna.
Dlaczego tak się dzieje
Zwykle przez zbyt szeroką regułę cache: add_header Cache-Control "public, max-age=3600" w nginx zastosowane do wszystkich ścieżek, włącznie z tymi, które tworzą sesję; reguła „Cache Everything" w Cloudflare; albo wpis routeRules z nagłówkami cache dla '/**' w aplikacji, która ustawia ciasteczka na stronie głównej. Większość CDN domyślnie odmawia zapisywania odpowiedzi z Set-Cookie, nginx też, chyba że każesz mu ignorować ten nagłówek; ale jawne public to Ty mówiący im, żeby to zrobiły.
Jak to naprawić
- Odpowiedzi tworzące lub odnawiające sesję niosą
Cache-Control: private, no-store. Tak domyślnie robiąexpress-session, Laravel, Django i Rails; jeśli widziszpublic, dodała go późniejsza warstwa. - Rozdziel reguły cache według typu ścieżki.
publicz długimmax-agedla plików statycznych z hashem w nazwie (/_nuxt/,/_next/static/,/assets/);privatelubno-storedla HTML-u zależnego od użytkownika. W nginx używaj osobnych blokówlocation; w Nuxt i Next.js reguł per ścieżka zamiast jednej globalnej. - Nie ustawiaj ciasteczka sesji na publicznych, cache'owalnych stronach. Jeśli strona główna nie potrzebuje sesji, niech jej nie tworzy: utwórz ciasteczko przy logowaniu albo przy wejściu do panelu.
- Jeśli używasz CDN z „Cache Everything" lub podobnym, dodaj regułę, żeby nie cache'ować odpowiedzi z
Set-Cookieani ścieżek uwierzytelnionych. I dodajVary: Cookiejako dodatkową siatkę, choć nie zastępuje powyższego.
Sprawdź, czy naprawione
curl -sI https://twojadomena.pl | grep -iE "^(set-cookie|cache-control|vary):"
curl -sI https://twojadomena.pl/dashboard | grep -iE "^(set-cookie|cache-control):"
Jeśli jest Set-Cookie z nazwą sesyjną, Cache-Control tej odpowiedzi musi być private, no-store lub no-cache, nigdy public. Wykonaj drugie żądanie z Twoim ciasteczkiem sesji i bez niego: uwierzytelniona odpowiedź nigdy nie może pojawić się bez niego.
Często zadawane pytania
Mój CDN mówi, że nie cache'uje odpowiedzi z ciasteczkami. Więc to fałszywy alarm?
Wyciek prawdopodobnie dziś nie następuje, ale zależysz od domyślnego zachowania, które nowa reguła albo zmiana dostawcy mogą wyłączyć. Nagłówek mówi, że to publiczne; napraw nagłówek i przestań polegać na wyjątku.
A ciasteczka analityczne lub zgody?
Nie są oznaczane: liczą się tylko nazwy wskazujące na sesję. Ciasteczko zgody na publicznej stronie z cache jest normalne. Jak powinny być skonfigurowane ciasteczka sesji (Secure, HttpOnly, SameSite), sprawdza niebezpieczne ciasteczka.
Moja sesja to token w localStorage, nie ciasteczko.
Wtedy ta kontrola nie ma zastosowania, ale token w pamięci przeglądarki tak: token dostępny z JavaScriptu ma inne problemy.