JWT: JSON WEB TOKEN: un nuovo modo per autenticare le nostre sessioni in modo sicuro e scalabile

← Torna a Guide

Estratto da: https://giuseppetoto.it/json-web-token-un-nuovo-modo-per-autenticare-le-nostre-sessioni-in-modo-sicuro-e-scalabile-ee7b481d8b27

Json Web Token (JWT) è uno standard, abbastanza recente, uscito all’inizio del 2015 che consente al server di scrivere in un messaggio (payload) chi è l’utente loggato (e altre info di contorno), incapsulare questo messaggio all’interno di un token e darlo al client che lo utilizzerà da ora in poi per le successive chiamate al server, informandolo così di chi sta effettuando la chiamata. Questo consentirà al server di avere già le informazioni di autenticazione direttamente nel token stesso, evitando così dover passare dal database o di usare le sessioni per memorizzare le informazioni sull’autenticazione.

PERCHE’ USARE JWT? Facciamo un passo indietro

Quando facciamo una login, siamo abituati a creare una sessione tra il server e il client e a memorizzare user_id all’interno della sessione, al fine di recuperare le informazioni di autenticazione nelle successive chiamate. Qui già sorge un piccolo problema: cosa succede se utilizziamo più server per bilanciare il carico del nostro sito? Se abbiamo creato la sessione quando eravamo sul server A e successivamente il bilanciatore di risorse ci porta sul server B la sessione sul server A che aveva i nostri dati di autenticazione la perdiamo (in realtà per avere sessioni condivise su più server le sessioni non si memorizzano più localmente ma si tendono a memorizzarle all’interno di uno spazio condiviso…su un database no-sql ad esempio).

Se invece stiamo progettando un sistema api vorremmo utilizzare un token per far riferimento a una particolare sessione di autenticazione. Quindi quando viene effettuata una chiamata all’api login, effettuo lato server un autenticazione, creo il token, lo memorizzo nel database associandolo ad utente (magari con un timestamp di scadenza) e lo restituisco al client. Il client passerà questo token nelle successive chiamate, il server accede al db e verifica a quale utente è associato quel token. OK già va un po’ meglio…in realtà è peggio…stiamo creando un collo di bottiglia tra i vari server e il DB poichè ad ogni richiesta api partirà una query al database per capire a quale utente è associato quel token, oltre che a creare un problema di sicurezza (il database potrebbe essere attaccato da qualche utente malintenzionato e recuperare tutti i nostri token di sessione)

Ma ecco così che entra in gioco Json Web Token.

L’idea che c’è alla base è che dopo l’autenticazione, il server prepara un token all’interno del quale racchiude un payload in cui viene dichiarato in maniera esplicita chi è l’utente loggato. Dentro il token, oltre il payload viene inserita la firma dal server(costituto dal payload stesso criptato con la sua chiave segreta in codifica hash 256). Il client riceve il token e se vuole sarà libero di leggere il payload contenuto ma non potrà modificarlo poichè se lo facesse il token sarà invalidato dal server. Il client dovrà comunicare al server il token ricevuto per tutte le successive chiamate in cui è richiesta l’autenticazione. Il server riceverà il token ed estrapolerà il payload ma prima si assicurerà che il token sia stato firmato e autentificato con la sua chiave privata. Poichè il token contiene il payload con tutte le informazioni necessarie all’autenticazione (es. iduser), il server potrà evitare di passare ogni volta dal database per verificare a quale utente corrisponde quel token (ottimo per la scalabilità). Entriamo un po’ più nel pratico.

COME VIENE COSTRUITO IL Json Web Token?

Potete fare subito delle prove e vedere come viene costruito usando questo tool: http://jwt.io/#debugger

Il token è costituito da 3 parti:

HEADER

6o29X3jlUMQPOxrs.png

è un oggetto json in cui sono racchiuse le informazioni principali dell’algoritmo utilizzato per per preparare la firma criptata e includerla nel token stesso. L’oggetto json viene codificato usando la funzione encodebase64 e come prima parte del nostro token abbiamo questo:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

PAYLOAD

Qui è contenuto l’oggetto json che contiene le informazioni relative alla “sessione di autenticazione” che vogliamo inglobare all’interno del token. Ad esempio con:

6o29X3jlUdfvdMQPOxrs.png

stiamo dicendo che l’utente loggato si chiama John Doe, il suo ruolo è amministratore e l’id è 1234567890! Come vedete le informazioni di autenticazione sono inglobate nel messaggio stesso.

Il payload viene anche esso codificato con la funzione encodebase64 ottenendo la seconda parte del nostro token:

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

Notate bene che possiamo sempre decodificare un messaggio in base64 con la funzione decodebase64.

SIGNATURE

6o29X3jlUdfvdMvdfvPOxrs.png

Qui c’è la parte più interessante, ovvero la firma con cui il server certifica che questo messaggio è stato scritto da lui e che non è stato modificato da nessun altro.

La firma come potete vedere non è altro che il risultato di una funzione hash 256 che prende in input la codifica base64 dell’header concatenandola con un punto alla codifica base64 del payload, il tutto codificato con la nostra “chiave segreta” che solo il server conoscerà!

Il risultato finale è la concatenazione di queste 3 parti appena viste:

6o29X3jlUdfvdMvddfvvPOxrs.png

Il server quindi restituirà questo token al client. Il client dovrà comunicare al server il token ricevuto per tutte le successive chiamate in cui è richiesta l’autenticazione. Il server riceverà il token ed estrapolerà il payload (in cui sono contenute le informazioni sull’utente loggato e che non risulta cmq essere criptato) ma prima si assicurerà che il token sia stato firmato e autentificato con la sua chiave privata. Poichè il token contiene il payload con tutte le informazioni necessarie all’autenticazione (es. iduser), il server potrà evitare di passare ogni volta dal database per verificare a quale utente corrisponde quel token (ottimo per la scalabilità). Non so l’hai notato ma ho ricopiato la parte di su ma scommetto che ora rileggendola sarà sicuramente più chiara :)

IL TUTTO E’ ASSOLUTAMENTE SICURO?

Assolutamente si…c’è però da fare un piccolo appunto. Esistono delle librerie che vi aiutano a implementare questo standard (potete trovare l’elenco completo qui per il vostro linguaggio preferito http://jwt.io/#libraries-io). L’autore fa solo alcune piccole avvertenze di come alcune librerie hanno implementato male lo standard e possono sorgere problemi di sicurezza (maggiori info qui: https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/). Ad oggi comunque la maggior parte delle librerie hanno fixxato il problema che comunque riguarda solo se, invece di usare una funzione di hashcode per firmare i nostri messaggi, utilizziamo un sistema a chiavi asincrone (che porta ulteriori vantaggi…ad esempio possiamo assicurarci che il messaggio sia stato effettivamente inviato dal server o ricevuto da un determinato client).