I cookies sono piccole stringhe di dati, memorizzate direttamente nel browser. Sono parte del protocollo HTTP, definito dalla specifica RFC 6265
I Cookies vengono solitamente impostati dal web-server utilizzando l’HTTTP-header Set-Cookie. In seguito
il browser li aggiunge automaticamente a ogni richiesta (o quasi) dello stesso dominio che sta utilizzando Cookie HTTP-header.
Uno degli usi più comune è l’autenticazione:
- In seguito al login, il server usa l’HTTP-header
Set-Cookiein risposta all’impostazione del cookie che abbia un unico “identificativo di sessione”. - La prossima volta quando la richiesta viene impostata nello stesso dominio, il browser invia il Cookie attraverso la rete
usando l’HTTP-header
Cookie. - Così il server sa chi ha fatto la richiesta.
Possiamo accedere ai cookies dal browser, utilizzando la proprietà document.cookie.
Esistono tanti altri trucchi riguardo i cookies e le loro opzioni. In questo capitolo andremo ad analizzarli nel dettaglio.
Leggere dal document.cookie
Il tuo browser colleziona cookies dagli altri siti? Vediamo:
// Nel dominio javascript.info, utilizziamo Google Analytics per scopi statistici,
// quindi dovreste trovare dei cookies
alert( document.cookie ); // cookie1=value1; cookie2=value2;...
Il valore di document.cookie consiste nelle coppie name=value, delimitate da ;. Ognuna rappresenta un cookie a se stante.
Per trovare un cookie particolare, possiamo dividere document.cookie con ;, e in seguito, trovare il nome corretto.
Possiamo usare una RegEx o una funzione degli array.
Lasciamo il compito in questione al lettore come esercizio. Inoltre, alla fine del capitolo, troverai delle funzioni che ti aiuteranno a manipolare i cookies.
Scrivere nel document.cookie
Possiamo scrivere nel document.cookie. Ma fate attenzione che questa non è una proprietà, ma piuttosto un metodo di accesso accessor (getter/setter). Un assegnazione a quest’ultimo è trattato in modo particolare.
Un’operazione di scritture nel document.cookie aggiorna solo i cookies menzionati all interno del documento stesso, ma non affligge altri cookies.
Per esempio, questo comando imposta un cookie con il nome user e il valore John:
document.cookie = "user=John"; // aggiorna solo il cookie chiamato 'user'
alert(document.cookie); // mostra tutti i cookies
Se provate ad eseguirlo, probabilmente vedrete diversi cookies. Questo perché l’operazione document.cookie=
non sovrascrive tutti i cookies. Imposta solo il cookie menzionato ovvero user.
Tecnicamente, il nome e il valore possono avere qualsiasi carattere, per mantenere la formattazione valida dovrebbero essere
giustificati utilizzando la funzione interna encodeURIComponent:
// il carattere speciale (spazio), necessita di essere codificato
let name = "my name";
let value = "John Smith"
// codifica del cookie come my%20name=John%20Smith
document.cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value);
alert(document.cookie); // ...; my%20name=John%20Smith
Ci sono alcune limitazioni:
-
La coppia
name=value, in seguito aencodeURIComponent, non dovrebbe essere più grande di 4kb. In modo da non memorizzare valori eccessivamente grandi in un cookie. -
Il numero totale di cookies per dominio è limitato a circa 20+, ma il limite esatto dipende dal browser.
I cookies hanno diverse opzioni, molte di queste sono importanti e dovrebbero essere impostate.
Le opzioni sono indicizzate dopo key=value, delimitate da ; in questo modo:
document.cookie = "user=John; path=/; expires=Tue, 19 Jan 2038 03:14:07 GMT"
path
path=/mypath
Il prefisso del path dell’URL deve essere assoluto. In questo modo sarà accessibile a tutte le pagine che appartengono allo stesso path. Di default, corrisponde al path corrente.
Se un cookie è impostato come path=/admin, è visibile nelle pagine /admin e /admin/qualcosa, ma non in /home o /adminpage.
Solitamente, dovremmo impostare path nella cartella principale: path=/ affinché il cookie sia accessibile da tutte le pagine del sito.
domain
domain=site.com
Un dominio definisce dove il cookie è accessibile. In realtà, ci sono delle limitazioni. Non possiamo impostare nessun dominio.
Di default, un cookie è accessibile solo nel dominio in cui è stato impostato. Cosi se il cookie è stato impostato da site.com, non lo otterremo su other.com
…Inoltre, non otterremo il cookie nel sotto dominio forum.site.com!
// su site.com
document.cookie = "user=John"
// su forum.site.com
alert(document.cookie); // nessun user
Non c’è un modo per rendere il cookie accessibile da un secondo dominio, ad esempio other.com, pertanto non riceverà mai il cookie impostato su site.com
Questa è una restrizione per motivi di sicurezza, per consentirci di immagazzinare dati sensibili nei cookies che dovrebbero essere disponibili solo in un sito.
…Ma se la nostra intenzione è di renderli accessibili anche a sotto domini come forum.site.com? Questo è possibile. Mentre impostiamo un cookie su site.com, dovremmo espressamente selezionare l’opzione domain nella cartella principale del dominio: domain=site.com:
// su site.com
// rende il cookie accessibile su ogni sotto dominio *.site.com:
document.cookie = "user=John; domain=site.com"
// dopo
// su forum.site.com
alert(document.cookie); // possiede un cookie user=John
Per ragioni storiche, domain=.site.com (un punto prima di site.com) funziona nello stesso modo, garantendo accesso al cookie dal sotto dominio. Questa è una vecchia notazione, dovrebbe essere utilizzata se dobbiamo supportare vecchi browsers.
Ricapitolando, l’opzione domain ci consente di rendere un cookie accessibile ai sotto domini.
expires, max-age
Di default, se un cookie non ha una di queste opzioni, sparisce quando il browser viene chiuso. Questo tipo di cookie vengono definiti “cookie di sessione”.
Affinché il cookie sopravviva una volta che il browser è stato chiuso, possiamo impostare le opzioni expires or max-age.
expires=Tue, 19 Jan 2038 03:14:07 GMT
La data di scadenza del cookie, quando il browser lo cancellerà automaticamente.
La data dovrà essere esattamente in questo formato, nel fuso orario GMT. Possiamo usare date.toUTCString per ottenerla. Per esempio, possiamo impostare il cookie affinché scada in un giorno:
// +1 giorno da ora
let date = new Date(Date.now() + 86400e3);
date = date.toUTCString();
document.cookie = "user=John; expires=" + date;
Se impostiamo expires a una data nel passato, il cookie verrà cancellato.
max-age=3600
Se zero o negativa, il cookie viene cancellato:
// cookie verrà cancellato +1 ora da ora.
document.cookie = "user=John; max-age=3600";
// delete cookie (let it expire right now)
document.cookie = "user=John; max-age=0";
secure
secure
Il cookie dovrà essere trasferito soltanto tramite HTTPS.
Di default, se impostiamo un cookie su http://site.com, allora apparirà su https://site.com e viceversa.
Questo accade poiché i cookies sono dominio.dipendenti, non distinguono i vari protocolli.
Con questa opzione, se un cookie è impostato su https://site.com, non apparirà quando lo stesso sito viene visitato da HTTP, come http://site.com. Cosi facendo, se un cookie ha contenuto sensibile che non dovrebbe mai essere inviato tramite protocollo HTTP non criptato, il flag secure è la cosa giusta da fare.
//assumendo sia on https:// now
// imposta il cookie sicuro (Solo accessibile se si utilizza HTTPS)
document.cookie = "user=John; secure";
samesite
Un altro attributo di sicurezza è samesite. Progettato per proteggere da attacchi XSRF (cross-site request forgery).
Per comprendere il funzionamento e quando è utile, analizziamo un attacco XSRF.
XSRF attack
Immagina di effettuare il login nel sito bank.com. In questo caso hai un cookie di autenticazione da quel sito. Il tuo browser lo invia a bank.com ad ogni richiesta, affinché ti riconosca e esegua tutte le operazioni finanziari personali.
Ora, mentre stai navigando sul web su un altra finestra, occasionalmente potresti imbatterti nel sito evil.com. Questo sito ha un codice JavaScript che invia un form <form action="https://bank.com/pay"> al sito bank.com con campi che iniziano una transazione verso l’account dell’hacker.
Il browser invia i cookies ogni volta che visiti il sito bank.com, anche se la richiesta proviene da evil.com. In questo modo, la banca pensa che sia tu a effettuare i pagamenti.
Questo processo è chiamato attacco “Cross-site Request Forgery” (in breve, XSRF).
Le banche sono protette da questo tipo di assalto. Tutte le richieste generate da bank.com hanno un campo speciale, chiamato “XSRF protection token” (token di protezione), che una pagina maligna non può generare o estrarre da una pagina remota (precisamente, il form può essere inviato da quella pagina, ma non può ricevere indietro i dati).
Inoltre il sito bank.com controlla questo token in ogni richiesta che riceve.
Questa protezione richiede tempo per essere implementata: dobbiamo assicurarci che ogni richiesta abbia il campo per il token, inoltre dobbiamo verificare tutte le richieste in entrata.
Accedere all’opzione cookie samesite
L’opzione per il cookie samesite provvede alla protezione da questo tipo di attacchi in modo diversi, che (in teoria) non dovrebbe richiedere l’utilizzo del “xsrf protection tokens”.
Sono presenti due possibili valori:
samesite=strict(same assamesitewithout value)
Un cookie con samesite=strict non viene mai inviato se l’utente proviene dall’esterno del sito stesso.
In altre parole, quando un utente apre un link da una mail o invia una richiesta da evil.com, o effettua qualsiasi operazione che origini da un altro dominio, il cookie non viene inviato.
Se i cookies di autenticazione possiedono l’opzione samesite, allora l’attacco XSRF non ha speranze di successo, poiché l’invio da evil.com arriverà senza cookies. Cosi bank.com non riconoscerà l’utente e non procederà con il pagamento.
Questa protezione è affidabile. Solo le operazioni che provengono da bank.com invieranno il cookie samesite, ad esempio una richiesta proveniente da una pagina all interno di bank.com
Però è presente un piccolo inconveniente.
Quando un utente apre un link legittimo per