JWT-kwetsbaarheden
CWE-347OWASP A07:2021Bijgewerkt 31 augustus 20266 min leestijd
JWT-kwetsbaarheden zijn fouten bij het ondertekenen of verifiëren van een JSON Web Token, waardoor een aanvaller een token kan vervalsen en een andere gebruiker kan worden. Bekende oorzaken zijn alg:none, HS256/RS256-verwarring, een overgeslagen handtekeningcontrole en genegeerde claims. Zet één algoritme vast, controleer alle relevante claims en houd de geldigheidsduur kort.
Een JSON Web Token reist bij elke aanvraag mee tussen de browser en uw API, en uw server vertrouwt alles wat hij uit dat token leest: wie de gebruiker is, welke rol hij heeft, wanneer de sessie afloopt. Dat vertrouwen houdt alleen stand zolang de handtekening correct wordt gecontroleerd. Gebeurt dat niet, dan herschrijft een aanvaller het token en wordt hij wie hij maar wil. Hieronder leest u welke JWT-fouten het vaakst voorkomen en wat u eraan doet.
Wat zijn JWT-kwetsbaarheden?
JWT-kwetsbaarheden zijn fouten in de manier waarop een applicatie een JSON Web Token (JWT) uitgeeft of verifieert: een compact, ondertekend bewijs van de identiteit en de claims van een gebruiker, dat de server bij het inloggen afgeeft en bij elke volgende aanvraag vertrouwt. Een token bestaat uit drie delen, gescheiden door punten: een header die het ondertekeningsalgoritme benoemt, een payload met de claims, en een handtekening over de eerste twee delen. Die handtekening is het enige wat het token aan uw server bindt. Breekt de verificatie ervan, dan worden de claims bewerkbaar.
Zie een JWT als een festivalbandje. De kleur en het hologram bewijzen dat een beveiliger het heeft uitgegeven, dus u loopt de poort door zonder opnieuw uw legitimatie te tonen. Dat werkt alleen als de beveiliger het hologram daadwerkelijk bekijkt. Een JWT-kwetsbaarheid is de beveiliger die vluchtig naar de kleur kijkt en iedereen doorlaat, of die een bandje accepteert dat een bezoeker zelf heeft ingekleurd.
De fouten vallen in een handvol patronen uiteen: het algoritme none accepteren, een symmetrisch algoritme met een asymmetrisch verwarren, een token decoderen zonder de handtekening te controleren, en de handtekening wel controleren maar nooit de standaardclaims zoals geldigheidsduur, doelgroep en uitgever nakijken. Een aparte fout is de payload als privé beschouwen en er geheimen in bewaren.
Hoe werkt een JWT-aanval?
Neem een API die de huidige gebruiker rechtstreeks uit het token leest bij elke aanvraag. De ontwikkelaar verifieert de handtekening, maar laat de details aan de bibliotheek over.
Kwetsbaar:
const jwt = require("jsonwebtoken");
const fs = require("node:fs");
const publicKey = fs.readFileSync("public.pem");
// Kwetsbaar: het algoritme komt uit de header van het token,
// en er wordt geen uitgever, doelgroep of geldigheidsduur afgedwongen.
function huidigeGebruiker(token) {
return jwt.verify(token, publicKey);
}
Omdat de geaccepteerde algorithms niet zijn vastgepind, vertrouwt de verifier op wat de header beweert. Uit dat ene verzuim volgen twee vervalsingen.
De eerste is de alg:none-aanval. Een aanvaller zet het header-algoritme op none, past de payload aan om zichzelf een beheerdersrol te geven, en stuurt een lege handtekening mee. Een verifier die none respecteert, slaat de handtekeningcontrole over en geeft de vervalste claims terug. Over de lijn ziet de aanvraag er zo uit, waarbij de afsluitende punt de lege handtekening markeert:
GET /account HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiIxMDAxIiwicm9sZSI6ImFkbWluIn0.
Gedecodeerd luidt de header {"alg":"none"} en de payload {"sub":"1001","role":"admin"}. Niets heeft het ondertekend, en niets hoefde dat.
De tweede is HS256/RS256-verwarring. Uw server ondertekent met een RSA-privesleutel en verifieert met de bijbehorende publieke sleutel, die per ontwerp niet geheim is. Een aanvaller neemt die publieke sleutel, ondertekent een token met HMAC-SHA256 en gebruikt de bytes van de publieke sleutel als gedeeld geheim, en zet de header op HS256. Een verifier die het algoritme uit de header kiest, voert HMAC uit met de publieke sleutel als geheim: precies de waarde die de aanvaller gebruikte, dus de handtekening klopt. Het asymmetrische schema klapt in elkaar tot een symmetrisch schema met een publieke waarde als sleutel.
De meest elementaire fout is nog eenvoudiger: decoderen zonder te verifieren. Een aanroep als jwt.decode(token) leest de claims en raakt de handtekening nooit aan, waardoor elk token dat de aanvaller intypt wordt geaccepteerd.
De oplossing pint één algoritme vast en controleert de claims die het token draagt.
Veilig:
const jwt = require("jsonwebtoken");
const fs = require("node:fs");
const publicKey = fs.readFileSync("public.pem");
// Veilig: één algoritme is vastgepind en de standaardclaims worden afgedwongen.
function huidigeGebruiker(token) {
return jwt.verify(token, publicKey, {
algorithms: ["RS256"],
issuer: "https://auth.example.com",
audience: "https://api.example.com",
maxAge: "15m",
});
}
Door algorithms vast te pinnen op ["RS256"] worden zowel none als elke HS256-vervalsing geweigerd, want HMAC is geen algoritme meer dat de verifier uitvoert. De opties issuer en audience binden het token aan uw dienst, zodat een geldig token dat voor een andere applicatie is uitgegeven wordt afgewezen. De optie maxAge, samen met de exp-claim, houdt een gestolen token maar kort bruikbaar.
Wat is de impact van JWT-kwetsbaarheden?
Een JWT is een bearer-credential die de hele API vertrouwt, dus één verificatiefout is meteen een authenticatie-omzeiling op elk endpoint tegelijk. Een aanvaller die een token vervalst, zet elke sub en elke role die hij wil: hij leest en wijzigt de gegevens van een andere gebruiker en promoveert zichzelf van een gewoon account naar een beheerder. Waar de applicatie het token vertrouwt voor autorisatiebeslissingen, is dit broken access control via de voordeur.
De zakelijke impact loopt van hoog tot kritiek. Volledige accountovername, diefstal van persoonsgegevens met meldplicht, en manipulatie van gegevens zitten aan de bovenkant van die schaal. Draagt de payload ook geheimen, dan verbreedt de blootstelling zich tot informatielekkage, want het token was al die tijd leesbaar. Een ondertekende JWT is op zichzelf niet in te trekken zodra hij is uitgegeven, dus een vervalst of gestolen token blijft geldig tot het verloopt, wat precies verklaart waarom een korte geldigheidsduur telt.
Hoe spoort u JWT-kwetsbaarheden op?
Begin met het decoderen van een echt token: splits het op de punten en decodeer de eerste twee delen met base64url. Lees het header-algoritme en de payload-claims, en let op of exp, aud en iss aanwezig zijn en worden afgedwongen. Een token dat nooit verloopt, of een token dat voor een andere doelgroep is uitgegeven maar hier toch wordt geaccepteerd, is al een bevinding.
Test daarna de verifier rechtstreeks. Verander het header-algoritme in none en verwijder de handtekening. Onderteken een RS256-token opnieuw met HS256 en gebruik de publieke sleutel als geheim. Wijzig één claim en kijk of de wijziging wordt geaccepteerd. Verwijder of vervroeg exp en speel een oud token opnieuw af. Gereedschap zoals de JWT Editor-extensie in Burp Suite of het open-source jwt_tool automatiseert elk van deze proeven.
Scanners signaleren de voor de hand liggende gevallen, maar de interessante zitten achter de login en hangen af van hoe elk endpoint het token leest. AssistSec onderzoekt de uitgifte en verificatie van JWT’s in het kader van een penetratietest en toont per bevinding het exacte vervalste token dat de applicatie accepteerde.
Hoe voorkomt u JWT-kwetsbaarheden?
- Pin het algoritme vast. Geef de verifier een expliciete allowlist van precies één algoritme mee, bijvoorbeeld
algorithms: ["RS256"], en accepteernonenooit en laat het token nooit kiezen. - Verifieer elke standaardclaim. Dwing
expennbfaf voor de timing, en controleeraudenisszodat een elders uitgegeven token niet tegen uw dienst kan worden hergebruikt. - Houd de geldigheidsduur kort. Geef access tokens minuten en geen dagen, en gebruik een apart refresh token voor langere sessies zodat een gelekt token snel verloopt.
- Bescherm en scheid de sleutels. Gebruik een lang, willekeurig geheim voor HS256, houd privesleutels buiten de repository, en roteer sleutels met een gepubliceerde
kidzodat u een gecompromitteerde sleutel kunt intrekken. - Bewaar nooit geheimen in de payload. Behandel het token als publieke data en grijp naar een versleuteld token (JWE) als de inhoud echt verborgen moet blijven.
- Gebruik een onderhouden bibliotheek en houd die actueel. Vertrouw op een beproefde implementatie in plaats van zelfgebouwde parsing, en werk die bij, want oudere versies van populaire bibliotheken accepteerden
nonestandaard.
Bronnen
Veelgestelde vragen
Is een JWT versleuteld?
Nee, standaard niet. Een gewone ondertekende JWT is alleen base64url-gecodeerd, dus iedereen die het token bezit kan de header en de payload lezen. De handtekening bewijst dat het token niet is gewijzigd, maar verbergt de inhoud niet. Moet de payload geheim blijven, gebruik dan een versleuteld token (JWE) en zet nooit wachtwoorden of sleutels in een gewone ondertekende JWT.
Wat is de alg:none-aanval?
De header van een JWT benoemt het algoritme van de handtekening. Accepteert een verifier het algoritme none, dan slaat hij de handtekeningcontrole volledig over. Een aanvaller zet de header op alg:none, past de payload naar believen aan, stuurt een lege handtekening mee en wordt geaccepteerd als wat de payload beweert. De verdediging is het algoritme vastpinnen en none nooit toestaan.
Hoe werkt HS256/RS256-verwarring?
Bij RS256 ondertekent de server met een privesleutel en verifieert met de publieke sleutel, die niet geheim is. Leest de verifier het algoritme uit het token in plaats van het vast te pinnen, dan ondertekent een aanvaller een token met HS256 en gebruikt de publieke sleutel als HMAC-geheim. De verifier voert HMAC uit met diezelfde publieke sleutel en de vervalste handtekening klopt. Het algoritme vastpinnen op RS256 voorkomt dit.
Hoe lang mag een JWT geldig zijn?
Houd access tokens kort, in de orde van enkele minuten tot ongeveer een kwartier, en geef een apart refresh token uit voor langere sessies. Een korte geldigheidsduur beperkt hoelang een gestolen of vervalst token bruikbaar blijft, want een ondertekende JWT is op zichzelf niet in te trekken zodra hij is uitgegeven. Controleer de exp-claim altijd tijdens de verificatie.
Verwante artikelen
- KwetsbaarhedenCWE-284A01:2021Broken access controlBroken access control uitgelegd: horizontale en verticale escalatie, forced browsing, en hoe u met deny by default serverzijdig autoriseert.
- KwetsbaarhedenCWE-287A07:2021Broken authenticationBroken authentication uitgelegd: hoe aanvallers via brute force, gelekte wachtwoorden en voorspelbare sessietokens accounts overnemen, en wat u eraan doet.
- KwetsbaarhedenCWE-327A02:2021Cryptographic failuresCryptographic failures uitgelegd: MD5 voor wachtwoorden, ECB-modus, vaste IV's en eigen crypto, en waarom Argon2id en AES-GCM het wel goed doen.
- KwetsbaarhedenCWE-384A07:2021Session fixationSession fixation uitgelegd: hoe een aanvaller vooraf een session id vastlegt, waarom die na het inloggen geldig blijft en hoe sessierotatie het stopt.