Quando un utente accede a un sito, nella maggior parte dei casi il server gli consegna un cookie di sessione: un identificativo che il browser rimanderà a ogni richiesta successiva per dimostrare "sono io, ho già fatto l'accesso". Chi entra in possesso di quel valore può presentarsi come l'utente. Per questo gli attributi con cui il cookie viene creato non sono dettagli: decidono chi può leggerlo, quando viene inviato e per quanto tempo resta valido.
Come nasce un cookie
Il server imposta un cookie con l'header Set-Cookie:
Set-Cookie: sessione=a8F3kq92Lm; Path=/; Secure; HttpOnly; SameSite=Lax
Prima del punto e virgola ci sono nome e valore; dopo, gli attributi. Il browser memorizza il cookie e lo allega automaticamente alle richieste successive che rispettano le condizioni indicate.
Secure
Set-Cookie: sessione=...; Secure
Il cookie viene inviato solo su connessioni HTTPS. Senza questo attributo, una singola richiesta in chiaro, per esempio dovuta a un link http:// dimenticato, esporrebbe il valore a chiunque possa osservare il traffico. Per i cookie di sessione è indispensabile.
HttpOnly
Set-Cookie: sessione=...; HttpOnly
Il cookie non è accessibile da JavaScript: non compare in document.cookie. Se un attaccante riesce a iniettare uno script nella pagina tramite una vulnerabilità di cross-site scripting, non potrà leggere il cookie di sessione e portarlo altrove.
Non è una protezione completa contro l'XSS, perché lo script iniettato può comunque compiere azioni nella pagina a nome dell'utente, ma impedisce il furto della sessione da riutilizzare in seguito. I cookie di sessione dovrebbero essere sempre HttpOnly; solo i cookie che il tuo JavaScript deve davvero leggere, come una preferenza di tema, possono farne a meno.
SameSite
Questo attributo controlla se il cookie viene inviato nelle richieste che partono da altri siti. È la principale difesa moderna contro il CSRF (Cross-Site Request Forgery), un attacco in cui una pagina malevola induce il browser dell'utente a inviare una richiesta al tuo sito, completa di cookie.
| Valore | Comportamento |
|---|---|
Strict |
il cookie non viene mai inviato in richieste originate da altri siti, nemmeno seguendo un link |
Lax |
inviato quando l'utente segue un link verso il tuo sito, ma non in richieste POST, iframe o caricamenti originati da altri siti |
None |
inviato sempre; richiede obbligatoriamente anche Secure |
Lax è un buon equilibrio per la maggior parte dei cookie di sessione: l'utente che arriva da un link resta connesso, mentre i form inviati da altri siti non portano il cookie. Molti browser applicano già Lax quando l'attributo manca, ma è meglio dichiararlo esplicitamente, perché il comportamento predefinito non è identico ovunque.
Strict è più sicuro ma può sorprendere: chi clicca un link al tuo sito da un'email o da un altro sito arriva come se non fosse connesso.
Ricorda che SameSite riduce molto il rischio CSRF ma non sostituisce del tutto i token anti-CSRF per operazioni critiche, anche perché considera "stesso sito" anche i sottodomini dello stesso dominio.
Domain e Path
- Senza
Domain, il cookie viene inviato solo all'host esatto che lo ha impostato. È la scelta più restrittiva e, di solito, la migliore. - Con
Domain=esempio.it, il cookie viene inviato anche a tutti i sottodomini. Se uno di essi è gestito da terzi o è meno protetto, il cookie diventa esposto. Pathlimita i percorsi a cui il cookie viene inviato, ma non è un confine di sicurezza: pagine dello stesso host possono comunque accedere ai cookie di altri percorsi in vari modi. Non affidarti aPathper isolare dati sensibili.
Scadenza
- Senza
ExpiresnéMax-Age, il cookie è di sessione del browser: viene eliminato quando il browser lo ritiene chiusa la sessione (in pratica, molti browser ripristinano le sessioni e questi cookie possono sopravvivere a lungo). Max-Age=3600lo fa scadere dopo un'ora;Expiresindica una data precisa.
La scadenza nel browser non basta: la sessione deve scadere anche sul server, che dovrebbe rifiutare identificativi troppo vecchi o inattivi.
Il prefisso __Host-
Se il nome del cookie inizia con __Host-, il browser lo accetta solo se:
- ha l'attributo
Secure; - è stato impostato da una pagina HTTPS;
- non ha l'attributo
Domain; - ha
Path=/.
Set-Cookie: __Host-sessione=a8F3kq92Lm; Path=/; Secure; HttpOnly; SameSite=Lax
In questo modo un sottodominio compromesso non può sovrascrivere il cookie di sessione del dominio principale. Esiste anche il prefisso __Secure-, che richiede solo Secure.
Buone pratiche per la gestione della sessione
- Genera identificativi di sessione lunghi e casuali con un generatore sicuro, mai prevedibili o sequenziali.
- Rigenera l'identificativo dopo l'accesso: così un valore impostato prima del login non può essere riutilizzato da chi lo conosceva.
- Invalida la sessione sul server al logout, non limitarti a cancellare il cookie.
- Non memorizzare dati sensibili in chiaro nei cookie: il cookie contiene un identificativo, i dati restano sul server.
- Ricorda i limiti di dimensione: un cookie dovrebbe restare entro circa 4 KB, e tutti i cookie del dominio viaggiano in ogni richiesta, appesantendola.
Una configurazione consigliata
Per il cookie di sessione di un'applicazione web servita su un solo host:
Set-Cookie: __Host-sid=<valore casuale>; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=28800
In sintesi
Secureinvia il cookie solo su HTTPS;HttpOnlylo nasconde a JavaScript.SameSite=Laxè un buon valore predefinito contro il CSRF;NonerichiedeSecure.- Evita
Domainse non ti serve e non considerarePathun confine di sicurezza. - Il prefisso
__Host-impedisce ai sottodomini di sovrascrivere il cookie. - Rigenera la sessione dopo l'accesso e falla scadere anche sul server.