Wordt u aangevallen? Vraag hulp bij incidentrespons

info@angelforce.eu
Veilig ontwikkelen

Valkuilen bij JWT-beveiliging en hoe u ze vermijdt

Veelgemaakte JWT-fouten: alg none, verwarring tussen RS256 en HS256, zwakke HMAC-geheimen, ontbrekende claimcontroles, kid-injectie, intrekking en opslag.

Ook beschikbaar in:EnglishTürkçeDeutschFrançaisEspañolItalianoPolskiEesti

Illustratie: Valkuilen bij JWT-beveiliging en hoe u ze vermijdt

JSON Web Tokens (JWT’s) worden veel gebruikt voor authenticatie van API’s en voor single sign-on. Het formaat is eenvoudig, maar laat veel beveiligingskeuzes over aan de ontwikkelaar en de bibliotheek. RFC 8725, de JWT Best Current Practices, verzamelt de bekende fouten en hoe u ze vermijdt. In dit artikel lopen we de fouten langs die wij als eerste zouden controleren.

Een JWT is ondertekend, niet geheim

Een ondertekend JWT, gedefinieerd in RFC 7519, bestaat uit drie delen, gescheiden door punten: een header, een payload met claims en een handtekening. De eerste twee zijn alleen base64url-gecodeerd. Iedereen die het token heeft, kan ze lezen. Hier is een gedecodeerd voorbeeld:

{
  "alg": "RS256",
  "typ": "at+jwt",
  "kid": "2026-10-key-1"
}
{
  "iss": "https://auth.example.eu",
  "sub": "user-4821",
  "aud": "api.example.eu",
  "iat": 1792140000,
  "exp": 1792140900,
  "role": "editor"
}

De handtekening bewijst dat de claims niet zijn gewijzigd. Ze verbergt ze niet. RFC 7519 stelt dat als een token privacygevoelige informatie bevat, er maatregelen moeten worden genomen om openbaarmaking te voorkomen, bijvoorbeeld door een versleuteld JWT (JWE) te gebruiken. Houd in de praktijk persoonsgegevens, interne kenmerken die u niet zou publiceren en alles wat geheim is buiten de payload.

Valkuil 1 - "alg": "none" accepteren

RFC 7519 staat "onbeveiligde" JWT’s toe met "alg": "none" en een lege handtekening. RFC 8725 beschrijft bibliotheken die zulke tokens accepteerden en de controle van de handtekening helemaal oversloegen. Een aanvaller kon dan elke gewenste claim invullen.

RFC 7518 bepaalt dat implementaties onbeveiligde tokens standaard niet mogen accepteren, en de OWASP REST Security Cheat Sheet zegt ze helemaal niet toe te staan. Controleer of uw bibliotheek none weigert, en test dat met een zelfgemaakt token.

Valkuil 2 - het token het algoritme laten kiezen

De waarde alg in de header wordt gekozen door wie het token heeft gemaakt, en dat kan een aanvaller zijn. RFC 8725 beschrijft de klassieke verwarringsaanval: een server verwacht RS256, geverifieerd met een publieke RSA-sleutel. De aanvaller verandert alg in HS256 en ondertekent het token met HMAC, met die publieke sleutel als geheim. Een bibliotheek die de header volgt, verifieert het token met succes, en de publieke sleutel is per definitie openbaar.

De oplossing is het algoritme op de server vast te leggen. Volgens OWASP mag de verificatie niet op de JWT-header vertrouwen om het algoritme te kiezen. RFC 8725 voegt daaraan toe dat elke sleutel met precies één algoritme moet worden gebruikt. In code ziet dat er ongeveer zo uit:

verify(token,
       key        = issuer_public_key,
       algorithms = ["RS256"],
       issuer     = "https://auth.example.eu",
       audience   = "api.example.eu")

Valkuil 3 - zwakke HMAC-geheimen

Bij HS256 kan iedereen die een token onderschept offline proberen het geheim te raden, zo snel als de hardware toestaat. RFC 8725 bepaalt dat wachtwoorden die mensen kunnen onthouden niet rechtstreeks als HMAC-sleutel mogen worden gebruikt. RFC 7518 eist een sleutel die minstens zo lang is als de uitvoer van de hashfunctie, dus 256 bits voor HS256.

openssl rand -base64 32

Dat levert 32 willekeurige bytes op, oftewel 256 bits. Bewaar die in een secrets manager, niet in de repository.

Gedeelde geheimen hebben nog een zwakte. Elke dienst die een HMAC-token kan verifiëren, kan er ook een maken. Daarom geeft OWASP de voorkeur aan handtekeningen boven MAC’s: als meerdere diensten tokens valideren, kan één gecompromitteerde dienst anders tokens voor alle diensten aanmaken. Met asymmetrische algoritmen zoals RS256 of ES256 hoeft u alleen de publieke sleutel te delen.

Valkuil 4 - claims niet valideren

Een geldige handtekening bewijst alleen wie het token heeft uitgegeven. De claims bepalen of deze dienst het token nu moet accepteren.

  • exp - volgens RFC 7519 mag het token op of na dit tijdstip niet meer worden geaccepteerd. Een kleine marge voor klokverschillen, meestal niet meer dan een paar minuten, is toegestaan.
  • nbf - weiger het token vóór dit tijdstip, als de claim aanwezig is.
  • iss - controleer of de uitgever degene is die u vertrouwt en of die eigenaar is van de ondertekeningssleutel.
  • aud - volgens RFC 7519 moet het token worden geweigerd als de dienst zichzelf niet in de doelgroep terugvindt.

OWASP raadt aan om voor toegangstokens van API’s standaard iss, aud en exp te eisen en te valideren. RFC 8725 legt uit waarom de doelgroep belangrijk is. Zonder doelgroep kan een token dat voor de ene dienst is uitgegeven, opnieuw worden gebruikt bij een andere dienst die dezelfde uitgever vertrouwt. RFC 8725 raadt ook expliciete typering aan met de header typ, zodat het ene soort token niet kan worden gebruikt waar een ander soort wordt verwacht. Voor OAuth-toegangstokens gebruikt RFC 9068 at+jwt, zoals in het voorbeeld hierboven.

Valkuil 5 - kid, jku en x5u vertrouwen

Headerparameters die de server vertellen waar de sleutel te vinden is, worden ook door de aanvaller bepaald. RFC 8725 noemt de risico’s:

  • kid (sleutel-ID) die wordt gebruikt bij een opzoeking in een database of directory, kan SQL- of LDAP-injectie mogelijk maken. Hetzelfde geldt als hij wordt gebruikt om een bestandspad op te bouwen.
  • jku en x5u verwijzen naar een URL om sleutels op te halen. Elke URL volgen maakt server-side request forgery mogelijk, en een aanvaller kan de URL naar zijn eigen sleutel laten wijzen.

Behandel kid als een betekenisloos label en zoek het op in een vaste lijst sleutels die u zelf beheert. Haal sleutels alleen op van URL’s op een allowlist; RFC 8725 voegt daaraan toe dat bij die verzoeken geen cookies mogen worden meegestuurd.

Valkuil 6 - lang geldige tokens en geen intrekking

Een JWT blijft geldig tot het verloopt, ook nadat de gebruiker is uitgelogd of een beheerder het account heeft uitgeschakeld. OWASP merkt op dat tokens langer kunnen bestaan dan de sessie die ze vertegenwoordigen.

  • Houd toegangstokens kortlevend, gemeten in minuten in plaats van dagen.
  • Gebruik refreshtokens die de server bij elk gebruik controleert en kan intrekken.
  • Houd voor uitloggen en het blokkeren van accounts een denylist bij van token-ID’s (jti) totdat die tokens verlopen, zoals OWASP voorstelt.
  • Vervang ondertekeningssleutels volgens een vast schema, en zorg voor een getest proces voor een vervanging in noodgevallen.

Valkuil 7 - tokens opslaan waar scripts ze kunnen lezen

Tokens in localStorage zijn leesbaar voor elk JavaScript op de pagina. De OWASP HTML5 Security Cheat Sheet waarschuwt dat één enkele cross-site-scriptingfout alles in deze opslagobjecten kan stelen, en raadt af om er sessie-ID’s in te bewaren.

Voor browserapplicaties houdt een cookie met HttpOnly en Secure en een passende SameSite-instelling het token buiten bereik van scripts. Met cookies komt cross-site request forgery in beeld, dus houd daar ook rekening mee. Wat u ook kiest: een sterke Content Security Policy verkleint de kans op XSS in de eerste plaats.

Een checklist voor validatie

  1. Leg de geaccepteerde algoritmen op de server vast en weiger none.
  2. Gebruik een bibliotheek die de handtekening verifieert voordat u claims leest.
  3. Valideer exp, nbf, iss en aud bij elk verzoek.
  4. Gebruik typ of aparte doelgroepen om soorten tokens van elkaar te scheiden.
  5. Zoek sleutels op kid op in een vaste set; haal nooit sleutels op van willekeurige URL’s.
  6. Gebruik willekeurige HMAC-sleutels van minstens 256 bits, of asymmetrische sleutels.
  7. Houd payloads vrij van geheimen en onnodige persoonsgegevens.
  8. Houd toegangstokens kortlevend en zorg dat u ze kunt intrekken.
  9. Bewaar tokens in de browser buiten bereik van JavaScript.

Waar u begint

Plak een token uit uw testomgeving in onze JWT-decoder om precies te zien wat het prijsgeeft, en toets de waarden van alg, aud en exp aan deze lijst. Plak geen actieve productietokens in welke onlinetool dan ook.

Wilt u een second opinion over het ontwerp van uw authenticatie of een codereview van de manier waarop u met tokens omgaat? Ons team voor veilige software helpt u graag.

Hoe wij kunnen helpen

Veilige software

Wij bouwen beveiliging in software in vanaf de eerste commit, en wij ontwikkelen eigen securitytools.

JWT-decoder

Decodeer een JSON Web Token en controleer het op bekende beveiligingsfouten. Het token blijft in uw browser.

Neem contact op
AngelForce-team

Geschreven door het AngelForce-team: securityconsultants met CEH-, LPT Master- en CISA-certificering die systemen van organisaties in heel Europa en Turkije testen, bewaken en beschermen.

Geschreven met hulp van AI op basis van de openbare bronnen hieronder, en vóór publicatie gecontroleerd door het team van AngelForce.

Bronnen

  1. datatracker.ietf.org/doc/html/rfc7519
  2. datatracker.ietf.org/doc/html/rfc8725
  3. datatracker.ietf.org/doc/html/rfc7518
  4. cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html
  5. cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html

Artikelen over Veilig ontwikkelen

Vertel ons wat u wilt beschermen

Beschrijf uw omgeving en wat u wilt bereiken. Wij antwoorden per e-mail.

Neem contact op
info@angelforce.eu

AngelForce OÜ · Tallinn, Estland