Come funziona questo controllo
Scanaris guarda la risposta principale della tua pagina e verifica due condizioni insieme. Prima: la risposta imposta almeno un cookie (Set-Cookie) il cui nome inizia con sess, sid, auth, token, jwt, connect.sid, phpsessid o asp.net, cioè un cookie che con ogni probabilità identifica una sessione. Seconda: la stessa risposta porta Cache-Control con la direttiva public. Solo se valgono entrambe apre un rilievo di gravità media con il nome del cookie e il valore dell'header. È deliberatamente rigoroso: un cookie di preferenze con cache pubblica, o un cookie di sessione senza public, non vengono segnalati.
Perché è importante
Cache-Control: public è un'istruzione esplicita a qualsiasi cache condivisa (una CDN, un reverse proxy come Varnish o nginx, il proxy di un'azienda o di un operatore) che quella risposta può essere conservata e consegnata ad altri utenti. Se quella stessa risposta include un Set-Cookie di sessione, la cache può conservare il cookie insieme al corpo e darlo al visitatore successivo. Quel visitatore entra con la sessione del precedente, senza password, e vede i suoi dati. È una falla di quelle che si scoprono quando un cliente scrive dicendo che "è entrato nell'account di un'altra persona". È successo a banche e grandi negozi, sempre per la stessa combinazione: contenuto personalizzato marcato come cacheabile.
Perché succede
Di solito per una regola di cache troppo ampia: un add_header Cache-Control "public, max-age=3600" in nginx applicato a tutte le rotte, incluse quelle che creano sessione; una regola "Cache Everything" su Cloudflare; o un routeRules con header di cache per '/**' in un'app che imposta cookie nella pagina principale. La maggior parte delle CDN rifiuta di conservare risposte con Set-Cookie per impostazione predefinita, e nginx pure, a meno che tu non gli dica di ignorare quell'header; ma un public esplicito sei tu che dici loro di farlo.
Come risolvere
- Le risposte che creano o rinnovano sessione portano
Cache-Control: private, no-store. È ciò che fanno di defaultexpress-session, Laravel, Django e Rails; se vedipublic, l'ha aggiunto uno strato successivo. - Separa le regole di cache per tipo di rotta.
publicconmax-agelungo per i file statici con hash nel nome (/_nuxt/,/_next/static/,/assets/);privateono-storeper l'HTML che dipende dall'utente. In nginx, usa blocchilocationdistinti; in Nuxt e Next.js, regole per rotta invece di una globale. - Non impostare il cookie di sessione in pagine pubbliche cacheabili. Se la home non ha bisogno di sessione, non la crei: crea il cookie al login o all'ingresso nel pannello.
- Se usi una CDN con "Cache Everything" o simile, aggiungi una regola per non mettere in cache le risposte con
Set-Cookiené le rotte autenticate. E aggiungiVary: Cookiecome rete ulteriore, anche se non sostituisce quanto sopra.
Verifica che sia risolto
curl -sI https://tuodominio.it | grep -iE "^(set-cookie|cache-control|vary):"
curl -sI https://tuodominio.it/dashboard | grep -iE "^(set-cookie|cache-control):"
Se c'è un Set-Cookie con nome di sessione, il Cache-Control di quella risposta deve essere private, no-store o no-cache, mai public. Fai la seconda richiesta con e senza il tuo cookie di sessione: la risposta autenticata non deve mai comparire senza.
Domande frequenti
La mia CDN dice che non mette in cache risposte con cookie. Allora è un falso positivo?
Probabilmente la fuga oggi non avviene, ma dipendi da un comportamento predefinito che una regola nuova o un cambio di fornitore possono disattivare. L'header dice che è pubblico; correggi l'header e smetti di dipendere dall'eccezione.
E i cookie di analitica o di consenso?
Non vengono segnalati: contano solo i nomi che indicano sessione. Un cookie di consenso in una pagina pubblica con cache è normale. Come devono essere configurati i cookie di sessione (Secure, HttpOnly, SameSite) lo guarda cookie insicuri.
La mia sessione è un token in localStorage, non un cookie.
Allora questo controllo non si applica, ma token nello storage sì: un token accessibile da JavaScript ha altri problemi.