De je Weet Toch?! tokens

El Niño
6 min readApr 25, 2017

--

Nee, ze heten helemaal niet zo! JWT-Tokens, oftewel JSON Web Tokens is een slimme manier om sessies tussen een API en een (web)applicatie te behouden.

Elk API- of backendsysteem moet met tokens of sessies werken om te kunnen verifiëren of de bezoeker toegang heeft tot een bepaalde (beveiligde) URL of onderdeel van een systeem. Normaal gesproken krijg je als bezoeker automatisch een cookie + sessie ID toegekend zodra je een systeem of website benadert. Dit doen PHP of andere programmeertalen met een paar eenvoudige stappen automatisch voor je. Met JWT-Tokens gaat het toch iets anders en beter!

Hoe het vroeger ging

Om te zorgen dat het systeem bij kan houden welke gebruiker je bent wordt je Sessie ID die op de server is aangemaakt opgeslagen in een cookie in je browser. Op die manier kunnen alle verzoeken die je via de browser verstuurt gekoppeld worden aan je (ingelogde) sessie en dus ook aan jouw account (en de daarbijbehorende rollen en rechten).

Een aantal belangrijke zaken zoals bijvoorbeeld je Gebruiker ID, IP-adres en Browser agent kunnen in zo’n sessie op de server worden opgeslagen. Elk Sessie ID wordt namelijk ‘onder de motorkap’ automatisch gekoppeld aan een fysiek bestand op de server waar de PHP-applicatie op draait. Veranderingen aan je sessie worden dus ook fysiek doorgevoerd in een bestand op de server. Deze bestanden worden op Linux over het algemeen in de /tmp map opgeslagen.

Bij elk verzoek dat je vanuit de browser verstuurt wordt je sessie gevalideerd door het sessiebestand op de server te openen, uit te lezen en te controleren op basis van een aantal kenmerken (bijv. ip-adres, browser-agent en referrer).

Het is ook mogelijk om dit sessiebestand niet fysiek op de server op te slaan, maar gebruik te maken van een caching server/mechanisme zoals Redis. Hiermee verplaats je eigenlijk het bestand naar een entry in je caching-server die eigenlijk zoveel mogelijk informatie in het werkgeheugen van de server opslaat. Voordeel is natuurlijk dat de sessiegegevens sneller uitgelezen kunnen worden ten opzichte van een fysiek bestand en dit komt dus de snelheid van je (web)applicatie ten goede.

Normaal gesproken wordt bij een webapplicatie met beveiligde URL’s bij elk verzoek de sessie van de gebruiker geraadpleegd. Deze wordt gebruikt om te bepalen of je een pagina mag opvragen. Indien dit het geval is wordt die pagina ook getoond, anders kan je bijvoorbeeld doorgestuurd worden naar de inlogpagina. Als het checken van je sessie lang duurt dan zal dat de gehele laadtijd van de pagina niet ten goede komen. Overigens heeft dit natuurlijk ook impact op de bezoekers die niet zijn ingelogd. Immers, voor deze gebruikers wordt ook over het algemeen de sessie uitgelezen om bijvoorbeeld te bepalen in welke taal de pagina getoond moet worden.

Hoe we het nu doen!

Omdat we tegenwoordig steeds meer applicaties opdelen in twee delen (frontend en een API) zijn we gaan kijken hoe we de sessie tussen een frontend applicatie en de API beter kunnen beveiligen zonder dat het ten koste gaat van de snelheid. We kwamen uit op JWT: www.jwt.io.

Het basisconcept is hetzelfde alleen worden je sessie ID en sessiegegevens gecombineerd in een lange reeks van tekens. Normaal gesproken bestaat een PHP sessie ID uit bijvoorbeeld:

69978b703c489588f123d9b4bf5e9d4e3da3594f

Een sessie ID bij het gebruik van JWT is anders, namelijk:

eyJhbGciOiJSUzI1NiJ9.eyJmaXJzdF9uYW1lIjoiTWljaGFlbCBBbmdlbG8iLCJsYXN0X25hbWUiOiJHcm9lbmV2ZWxkIiwiaWQiOjEwNiwic2VjdXJpdHlfcm9sZXMiOnsidXNlciI6M30sImFwcF90b2tlbiI6dHJ1ZSwiZXhwIjoxNTA0MzA1MzY3LCJpYXQiOjE0ODg1MjY5MDR9.QfgK2WG0LpfafRdlMJQd0_NFJN2WpuinvXkdoXb2ZxtYirl8i9oGDdEvZGjnHEmkArjTREwlx3ZbHgp29pLZerYlA4St8z2jLRh5nValoLHMvj6c8qq34ZXsHYrUJLjYqNqcZWeFWk1us6cfkJcQBhdzKd8KAMkm2ieH6aBIo7ZL0SL5XTrbPXzsX_yXt4NDwRGkNxmkI455nlqDLn6RpNhfG6ayE5ZW9YannvAq_CVAsQNnlKP5kqp5px_QszOmyNRUvaOQbJQ3t35rtHrFQArXuGnW6hAGkVbS_gSVy8BKzmoX64wkgIm8MDHqRbukcV4nhEDF5E52hKbbf0nlhHUh0-lfqhnwT7VyTBa85sJE_cVpSHmkaa4CfhHIMxiHYYn0azNBtC0kS29cpNj8vvyjkzbXZDebJUe7MzoEO265LI2va1KDfmBT-SNBcvFG91Mxz_qNFs9uhLH4tnLevO_UzITI_Z1Mkd5v-fCYt58FpZb9hHhY42ITL4xo0eJJjCXy-AnZATx_kKUk3WUlxKOn1o7WSAaeMaVMSm8r5RjyVdlbnRXq2TN5EAsKqkKnDpGKonncdkmXZdWYW9GjoEZJBW7n_zzTh_iaXdVMvALFTrmtTmTSymxDb5sPKSasNi0SJx4kVLxFcgnhzxOf5TMPaePAaYLp5tkWGj07CZc

Voordeel hiervan is dat alles wat de API nodig heeft om je Sessie te (in)valideren is verwerkt in je sessie ID (oftewel token).

Als je bijvoorbeeld bovenstaande reeks tekens invult in de validator op jwt.io, kan je deze reeks decoderen tot wat bruikbare informatie in JSON-formaat:

{
"first_name": "Michael Angelo",
"last_name": "Groeneveld",
"id": 106,"security_roles": {
"user": 3
},
"app_token": true;
"exp": 1504305367,
"iat": 148852690
}

Wat je in zo’n token opslaat mag je helemaal zelf weten, in ons geval is het handig om het account ID, voor- en achternaam, rollen, aanmaaktijdstip (iat) en expiratietijdstip (exp) op te slaan (dit laatste komen we later op terug). Deze gegevens worden ‘claims’ genoemd en kunnen door je gehele applicatie worden gebruikt.

Door een JWT-decoder te integreren in de API-laag kunnen we deze tokens eenvoudig decoderen en meteen (in)valideren. Er zijn talloze libraries die ons hiermee kunnen helpen, zoals bijvoorbeeld: https://github.com/lexik/LexikJWTAuthenticationBundle, die we ook gebruiken bij onze Symfony 2 applicaties.

Grootste voordeel is dat we geen fysiek bestand meer hoeven te raadplegen (of eentje uit de cache) en dat sessies heel snel kunnen worden gecheckt, wat natuurlijk gunstig is voor de snelheid van de applicatie.

Veiligheid

De oplettende lezer zal zich afvragen hoe veilig dit is aangezien je in theorie de inhoud van de token als bezoeker/gebruiker kunt manipuleren en op die manier je ‘eenvoudig’ voor kunt doen als iemand anders. In principe klopt dit wel, tenzij je als ontwikkelaar gebruik maakt van certificaten om daarmee je token te ‘signen’. Dit is standaard mogelijk met JWT-tokens. Het principe is vergelijkbaar met het verifiëren van SSL-certificaten tussen de browser en de server.

Gebruik van JWT-Tokens bij API’s

De flow om een JWT-token te verkrijgen is door eerst de gebruiker te identificeren als geldige gebruiker middels een POST-request naar een API-endpoint met in de payload van de request een geldig e-mailadres en wachtwoord. Indien deze gegevens kloppen retourneert het endpoint een nieuw JWT-token. Elke keer dat het API-endpoint wordt aangeroepen krijg je steeds een nieuw token met daarin claims zoals bijvoorbeeld hierboven beschreven.

Bij elk volgende request richting de API moet de verkregen token meegegeven worden zodat de API de gebruiker kan verifiëren. Dit kan gedaan worden middels de Authorization header. Bijvoorbeeld:

Authorization: Bearer {token}

Waarbij {token} vervangen wordt door het JWT-token die de login endpoint had teruggegeven.

Geldigheidsduur van tokens

Nu blijft deze token geldig t/m de unix timestamp opgegeven in de claim ‘exp’. De API leest deze claim uit bij elk request en controleert of het tijdstip nog in de toekomst ligt. Mocht dit niet het geval zijn dan moet de API dit uiteraard teruggeven door bijvoorbeeld een 401 response code terug te geven. Vervolgens moet de frontend bijv. nog een keer inloggen om een nieuwe token te verkrijgen die weer langer geldig is.

Nadeel van een stateless token is dat elk token dat gegenereerd wordt in principe geldig is tot het moment dat het tijdstip in de claim ‘exp’ verloopt. Voor die tijd kan in principe elke gebruiker gebruik maken van het token. Het is namelijk niet mogelijk om een token te verwijderen als dusdanig (in PHP is dit wel mogelijk door session_destroy() aan te roepen).

Om een vergelijkbaar mechanisme te kunnen hanteren bij JWT-tokens, moeten we zelf dit mechanisme implementeren. De meest simpele variant is om alle tokens van uitgelogde gebruikers op te slaan en deze lijst te hanteren bij het valideren van een token die meegestuurd wordt bij een verzoek. Mocht het meegegeven token bestaan in het lijstje met verwijderde tokens, dan zou de API een 401 http code moeten retourneren om aan te geven dat de token niet (meer) geldig is.

Conclusie

Als je als ontwikkelaar tegen de beperkingen van standaard sessies aanloopt door bijvoorbeeld snelheid, veiligheid en ingewikkelde constructies om sessies te valideren raden we je aan om gebruik te maken van JWT-tokens. Er zijn genoeg open source projecten die eenvoudig geïntegreerd kunnen worden in je applicatie om JWT-tokens te ondersteunen.

Door Michael Angelo Groeneveld. Michael is leider bij El Niño en is verantwoordelijk voor de interne en externe kennisontwikkeling gerelateerd aan webtechnologieën.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

El Niño
El Niño

Written by El Niño

http://www.elnino.tech. Digital Development Agency building tailor made solutions, ensuring success by making it measurable.

No responses yet

Write a response