JSON Web Token e Laravel - Parte 1 - JWT-Cosa?

Mai sentito parlare di JWT, JSON Web Token? No? Bene, è arrivato il momento di rimediare. Basta poco per migliorare enormemente la propri applicazione!
francesco
Francesco Malatesta
02/09/2015 in Tutorial

Hai mai sentito parlare di JWT, o JSON Web Token? No? Strano. Beh, in realtà se sono qui, oggi, è proprio per questo. Nonostante non sia una cosa uscita nell'ultimo mese, molte persone non ne sono a conoscenza. Per questo motivo mi sono detto "perché non scriverci un bell'articolo che faccia anche da panoramica?"

Ed eccomi qua.

JWT-cosa?

Come ho appena detto, JWT sta per JSON Web Token. Si tratta di uno standard che viene usato per "regolare" le richieste tra due parti. Due parti che possono essere un'applicazione ed il server a cui fa riferimento, come la tua app di Facebook sul tuo telefono ed il server corrispondente dall'altra parte del mondo.

Sai già che oggi, molte applicazioni, soprattutto quelle più avanzate, espongono delle API per l'accesso ai propri servizi.

Prima di iniziare, però, credo sia necessario analizzare il passato per capire il "futuro". Stiamo parlando di autenticazione, ma come abbiamo gestito l'autenticazione finora?

C'era Una Volta...

Molti di noi, probabilmente, fino ad oggi hanno lavorato con il metodo più tradizionale di autenticazione, ovvero il metodo che prevede di salvare sul server tutte le informazioni riguardanti le sessioni.

La trafila è la seguente:

  • l'utente accede alla pagina di login;
  • l'utente inserisce le sue credenziali;
  • il server controlla le credenziali e, se tutto va bene, salva delle informazioni in sessione;

Perchè? Semplice: il protocollo HTTP è stateless. Non prevede il salvataggio di dati. Se non siamo noi (o un server) a salvare delle informazioni aggiuntive a corredo di una richiesta, tutto quello che viene dopo la richiesta stessa è "qualcosa di nuovo". Un po' come se il server non ci avesse mai visto prima in tutta la sua "vita". Con questa procedura appena vista il server cerca di "ricordarci".

Fino a relativamente poco tempo fa, quindi, il problema era gestito così. Salvando le informazioni sul server e via.

Poi, però...

... il mondo è cambiato. Le applicazioni sono diventate più complesse. Sono nate le API, è nata la necessità di scalare in modo intelligente, così com'è aumentata la necessità di sicurezza. Non basta mai.

Le sessioni sono diventate, quindi, un bel problema. Perché? Faccio un esempio per comprendere meglio la cosa.

Immagina di avere un'applicazione. Su un server solo. Tanto è piccola e fa pochi numeri.

Poi, un giorno, le cose cambiano e l'applicazione cresce. Certo, aumenti i requisiti del tuo server, ma ad un certo punto ti scontri con la realtà: devi scalare e comprare altre macchine, bilanciando il carico in modo diverso. A questo punto compri altri due server su cui far girare la tua applicazione. Ti ritrovi, quindi, con un Server1, Server2 e Server3.

Usando il vecchio sistema di autenticazione, però, qualcosa va storto. Potrebbe verifcarsi, infatti, che:

  • un utente effettua l'accesso;
  • il load balancer sta facendo accedere l'utente sul Server1. Ed è qui che vengono creati i dati di sessione;
  • i volumi di traffico cambiano, ed il Server1 diventa off-limits per il nostro utente, che viene quindi spedito al Server3;
  • al momento di effettuare una richiesta su un server diverso da quello dell'accesso, il nostro utente si trova fuori dall'applicazione e deve effettuare nuovamente l'accesso;

Considerando inoltre che tutta questa procedura è generalmente trasparente all'utente, il povero malcapitato non potrà fare altro che chiedersi

Ma che cavolo è successo?

Ora, tutto sembrava perduto, ma...

Esce fuori JWT!

Si è capito che bisognava gestire le cose in modo diverso. Dopo tanto lavoro, si è giunti ad una conclusione: i JSON Web Token.

Stravolgono totalmente quello che hai visto finora.

Vediamo come, riproponendo lo stesso esempio visto prima con il nostro utente.

  • l'utente accede alla pagina di login, nella quale inserisce le credenziali;
  • il server controlla tali credenziali e, se tutto va bene, rilascia un token;
  • l'utente memorizza localmente questo token;
  • per tutte le richieste successive, l'utente rimanderà al server il token rilasciato;
  • il server verificherà il token ad ogni richiesta prima di procedere;

In questo modo cambia tutto: se i dati relativi alla "sessione" dell'utente li conserva l'utente stesso, l'applicazione può scalare senza problemi su tutti i server necessari!

Ad ogni modo, sono sicuro che in questo momento in testa comincia a girare in modo sempre più insistente una domanda...

Aspetta, ma è Sicuro?

Si, e ti spiego subito perché. Partiamo prima dalla sua struttura. Come il nome può suggerire, un JWT è divisibile in tre parti:

  • Header: l'intestazione. Contiene delle informazioni sul token stesso e su come è stato criptato;
  • Payload: il corpo vero e proprio che contiene dei dati "variabili" in base al contesto;
  • Signature: una firma che contiene un hash dell'header, del payload ed un "secret" conosciuto solo dai server dell'applicazione;

Le prime due parti, l'header ed il payload, vengono codificate nel formato Base64.

Header

{
  "typ": "JWT",
  "alg": "HS256"
}

Il cui corrispondente Base64 è

ew0KICAidHlwIjogIkpXVCIsDQogICJhbGciOiAiSFMyNTYiDQp9

Payload

{
  "iss": "laravel-italia.it",
  "exp": 1300819380,
  "name": "Francesco Malatesta"
  "id": 1
}

Il cui corrispondente Base64 è

ew0KICAiaXNzIjogImxhcmF2ZWwtaXRhbGlhLml0IiwNCiAgImV4cCI6IDEzMDA4MTkzODAsDQogICJuYW1lIjogIkZyYW5jZXNjbyBNYWxhdGVzdGEiDQogICJpZCI6IDENCn0=

Signature

La signature è un semplice hash calcolato da un algoritmo descritto nell'header (HS256 in questo caso), usando una parola chiave nota SOLO al server. Le stringa che viene "hashata" è la semplice concatenazione delle versioni Base64 di header e payload, divise da un punto.

Eccola:

ew0KICAidHlwIjogIkpXVCIsDQogICJhbGciOiAiSFMyNTYiDQp9.ew0KICAiaXNzIjogImxhcmF2ZWwtaXRhbGlhLml0IiwNCiAgImV4cCI6IDEzMDA4MTkzODAsDQogICJuYW1lIjogIkZyYW5jZXNjbyBNYWxhdGVzdGEiDQogICJpZCI6IDENCn0=

Ipotizzando che la chiave segreta scelta sia "wowsuchsecret", la nostra signature sarebbe:

159f34c787220efe7c888f58477d35317fa45c043e96ea4015676c8abee6db4d

Tutto questo calcolo viene effettuato dal server, che restituisce il token nella sua forma finale.

Ovvero:

ew0KICAidHlwIjogIkpXVCIsDQogICJhbGciOiAiSFMyNTYiDQp9.ew0KICAidHlwIjogIkpXVCIsDQogICJhbGciOiAiSFMyNTYiDQp9.ew0KICAiaXNzIjogImxhcmF2ZWwtaXRhbGlhLml0IiwNCiAgImV4cCI6IDEzMDA4MTkzODAsDQogICJuYW1lIjogIkZyYW5jZXNjbyBNYWxhdGVzdGEiDQogICJpZCI6IDENCn0=.159f34c787220efe7c888f58477d35317fa45c043e96ea4015676c8abee6db4d

Tale token può essere salvato senza problemi in locale. Nessuna preoccupazione per la sicurezza: la procedura di verifica, da parte del server, si occupa di controllare e verificare l'hash. Operazione che può effettuare solo il server, in quanto possiede la parola chiave segreta.

JWT e Laravel

Ok, mi hai convinto. Questa cosa mi piace un bel po'. Posso implementare un meccanismo di autenticazione del genere in Laravel senza troppe complicazioni di sorta?

Assolutamente si: nella prossima parte dell'articolo, che puoi trovare qui, spiegherò nel dettaglio come installare e configurare il package jwt-auth di tymondesigns, per poi analizzarne l'uso nella terza parte di questa mini-serie.