JWT vs Session: welk authenticatiemechanisme kiezen?
Een gebruiker authenticeren betekent bij elk verzoek weten wie hij is. Twee grote families staan tegenover elkaar: de serversessies, waarbij de server bijhoudt welke gebruiker is aangemeld, en de JSON Web Tokens (JWT), waarbij de identiteit reist in een ondertekend token dat door de client wordt meegedragen. De keuze beïnvloedt de beveiliging, de schaalbaarheid en het gemak om een gebruiker af te melden. Zo beslis je op basis van je context.
De serversessies (stateful)
Bij een sessie maakt de server bij het aanmelden een sessie-identificator aan, slaat de bijbehorende gegevens (identiteit, rollen, winkelmand) aan de serverzijde op (geheugen, Redis, database) en stuurt naar de browser een cookie die alleen die identificator bevat. Bij elk verzoek leest de server de cookie, vindt de sessie terug en weet wie aan het woord is.
- Status aan de serverzijde: de bron van waarheid blijft op de server, de client draagt slechts een ondoorzichtige verwijzing.
- Onmiddellijke intrekking: het verwijderen van de sessie aan de serverzijde meldt de gebruiker direct af.
- Cookie: automatisch verzonden door de browser, idealiter met
HttpOnly,SecureenSameSite.
De JSON Web Tokens (stateless)
Een JWT is een zelfdragend token bestaande uit drie delen die in base64url zijn gecodeerd en gescheiden door punten: een header, een payload (de claims, bijvoorbeeld de gebruikers-id en de vervaldatum) en een handtekening. De server ondertekent het token bij het aanmelden; daarna volstaat het de handtekening te verifiëren om de inhoud te vertrouwen, zonder iets op te slaan.
- Zonder status: alle benodigde informatie zit in het token, de server heeft geen gedeeld geheugen nodig.
- Overal verifieerbaar: elke dienst die de sleutel kent kan het token valideren, handig voor gedistribueerde architecturen en SSO.
- Tools: je kunt een token inspecteren met onze JWT-decoder, de handtekening controleren met de JWT-verifier of er een aanmaken met de JWT-generator.
Vergelijkingstabel
| Criterium | Serversessie | JWT |
|---|---|---|
| Status | Stateful (op server opgeslagen) | Stateless (door client gedragen) |
| Serveropslag | Vereist (Redis, database) | Geen |
| Intrekking | Onmiddellijk | Moeilijk vóór de vervaldatum |
| Horizontale schaalbaarheid | Gedeelde opslag nodig | Native |
| Verzonden omvang | Klein (een identificator) | Groter (ondertekende claims) |
| Cross-domein / SSO | Beperkend | Geschikt |
| XSS-oppervlak | Laag met HttpOnly-cookie | Hoog indien in localStorage opgeslagen |
Beveiliging: XSS, CSRF en intrekking
Beide benaderingen zijn veilig als ze goed worden geïmplementeerd, maar hun risico's verschillen.
- XSS: een sessiecookie met
HttpOnlyis ontoegankelijk voor JavaScript en dus beschermd tegen diefstal via injectie. Een JWT die inlocalStorageis opgeslagen is daarentegen leesbaar voor elk script, wat er een geliefd doelwit van maakt. Het JWT in eenHttpOnly-cookie opslaan tenietdoet dat voordeel van JWT maar herintroduceert het CSRF-risico. - CSRF: cookies worden automatisch verzonden en zijn dus kwetsbaar voor CSRF zonder bescherming (attribuut
SameSite, anti-CSRF-token). Een JWT die handmatig in deAuthorization-header wordt verzonden is niet betroffen. - Intrekking: dit is het zwakke punt van JWT. Omdat het zelfdragend is, kun je het niet vóór de vervaldatum ongeldig maken zonder een serverstatus opnieuw te introduceren (intrekkingslijst, blacklist). Een sessie wordt direct verwijderd.
Schaalbaarheid en architectuur
Op één enkele server zijn sessies triviaal. Zodra je de belasting over meerdere instanties verdeelt, moet elke instantie toegang tot de sessies hebben: er is gedeelde opslag (Redis) of sticky sessions nodig. JWT schittert hier, want elke instantie valideert het token zonder netwerkaanroep of gemeenschappelijke opslag.
- Microservices: een JWT verspreidt de identiteit van de ene dienst naar de andere zonder centrale database.
- Publieke API's en mobiel: JWT vermijdt het beheer van cookies aan de native clientzijde.
- Klassiek monoliet: de sessie blijft standaard eenvoudiger en veiliger.
Wanneer de een of de ander kiezen
Kies sessies wanneer
- Je een klassieke webapplicatie met serverrendering ontwikkelt
- Onmiddellijke intrekking kritiek is (bank, gezondheidszorg, back-office)
- Je standaard de veiligste oplossing wilt, met de minste valkuilen
- Je infrastructuur zonder moeite een gedeelde sessieopslag aankan
Kies JWT wanneer
- Je een API blootstelt die door SPA's, mobiel of derden wordt gebruikt
- Je een microservices-architectuur of SSO tussen domeinen hebt
- Je horizontaal moet schalen zonder gedeelde opslag
- Je akkoord gaat met het beheren van korte vervaltijden en het vernieuwen van tokens
Aanbeveling
Voor de meeste webapplicaties blijven serversessies de veiligste en eenvoudigste keuze: onmiddellijke intrekking, HttpOnly-cookie en geen tokenbeheer aan de clientzijde. Reserveer JWT voor gevallen waarin het ontbreken van status echte waarde brengt: stateless API's, mobiel, microservices, SSO.
Als je voor JWT kiest, houd dan een korte levensduur (enkele minuten) aan, gekoppeld aan een refresh token opgeslagen in een HttpOnly-cookie, en voorzie een intrekkingslijst voor gevoelige gevallen. Zo combineer je het beste van beide werelden.
Veelgestelde vragen
Is een JWT versleuteld?
Nee, standaard is een JWT alleen ondertekend, niet versleuteld. De payload is in base64url gecodeerd en leesbaar voor wie hem onderschept. Plaats nooit gevoelige gegevens in leesbare vorm in een JWT. Om de inhoud te versleutelen moet je JWE (JSON Web Encryption) gebruiken.
Waar een JWT aan de clientzijde opslaan?
Het veiligst is een HttpOnly-, Secure- en SameSite-cookie, die beschermt tegen diefstal via XSS. localStorage is eenvoudiger maar stelt het token bloot aan elk kwaadaardig script. Vermijd het voor tokens met hoge rechten.
Hoe een gebruiker afmelden met een JWT?
Omdat het token zelfdragend is, vereist een echte afmelding ofwel wachten op de vervaldatum, ofwel een intrekkingslijst aan de serverzijde bijhouden. Daarom gebruikt men korte levensduren en een refresh token dat wel kan worden ingetrokken.
Kun je sessies en JWT combineren?
Ja, dat is een gangbare praktijk: een JWT access token met korte levensduur voor de API-aanroepen, en een refresh token dat als een sessie wordt beheerd (opgeslagen en intrekbaar aan de serverzijde) om het access token te vernieuwen.