Je authenticatie is zojuist in productie stukgegaan. Gebruikers krijgen “Invalid Token”-fouten en je moet snel uitzoeken waarom. Je maakt de JWT open en het lijkt op onzin: drie blokken willekeurige tekens gescheiden door punten. De gegevens zitten erin, maar zonder een parser kun je ze niet lezen.
Een JWT-parser is een gespecialiseerde tool die de drie delen van een JSON Web Token — Header, Payload en Signature — opsplitst volgens de RFC 7519-standaard. Vanaf april 2026 decoderen deze parsers Base64URL-gecodeerde gegevens en verifiëren ze handtekeningen met secrets of public keys om ervoor te zorgen dat het token niet is gemanipuleerd, waarmee ze bedreigingen zoals de “alg: none”-aanval blokkeren.
Wat een JWT-parser echt doet
Beschouw een JWT-parser als een vertaler. Het neemt een lange, ondoorzichtige tekenreeks en zet deze terug om naar leesbare JSON-objecten. Dit is fundamenteel voor het beheren van gebruikersidentiteiten en het beveiligen van gegevensuitwisseling in moderne applicaties.
Intern zoekt de parser de twee punten (.) op die het token in drie secties verdelen:
| Sectie | Doel | Gecodeerd? | Leesbaar zonder key? |
|---|---|---|---|
| Header | Metadata: ondertekeningsalgoritme (HS256, RS256) | Base64URL | Ja |
| Payload | Claims: gebruikersgegevens, vervaldatum, rollen | Base64URL | Ja |
| Signature | Digitale verzegeling die authenticiteit bewijst | HMAC/RSA | Nee — vereist key |

Stapsgewijs decoderen: wat er vanbinnen gebeurt
Laten we een echt token doorlopen. Neem deze voorbeeld-JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4iLCJpYXQiOjE3MDAwMDAwMDB9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Stap 1: Splitsen op punten
[0] eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
[1] eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4iLCJpYXQiOjE3MDAwMDAwMDB9
[2] SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Stap 2: Base64URL-decode sectie [0] (Header)
{
"alg": "HS256",
"typ": "JWT"
}
Stap 3: Base64URL-decode sectie [1] (Payload)
{
"sub": "1234567890",
"name": "John",
"iat": 1700000000
}
Stap 4: Verifieer sectie [2] (Signature) — vereist de secret key
De parser neemt de Base64URL-gecodeerde header + “.” + payload en berekent vervolgens een HMAC-SHA256 met de secret. Als het resultaat overeenkomt met sectie [2], is het token authentiek.
Belangrijke beveiligingsopmerking: Base64URL is geen versleuteling
Een veelvoorkomende valkuil voor nieuwe ontwikkelaars is de aanname dat de gecodeerde header en payload zijn versleuteld. Dat zijn ze niet. Zoals JustUse.me aangeeft, maakt Base64URL-codering JSON gewoon veilig om via URL’s en headers te verzenden. Iedereen die het token heeft, kan de payload decoderen zonder wachtwoord of key.
Bewaar nooit gevoelige gegevens (wachtwoorden, BSN’s, API-sleutels) in een JWT-payload. Ze zijn zichtbaar voor iedereen die het token onderschept.
Handtekeningverificatie: de beveiligingspoort
Hoewel iedereen de gegevens van een token kan lezen, is handtekeningverificatie wat je systeem daadwerkelijk veilig houdt. Een JWT-parser leest niet alleen informatie — hij bewijst waar deze vandaan komt.
De parser berekent de handtekening opnieuw met behulp van de header, payload en een key, en controleert vervolgens of het resultaat overeenkomt met de handtekening op het token. Als ze niet overeenkomen, is het token gemanipuleerd.
Twee algoritmefamilies
| Algoritme | Key-type | Hoe het werkt | Veelvoorkomend gebruik |
|---|---|---|---|
| HS256 (HMAC) | Symmetrisch — dezelfde secret key voor ondertekenen en verifiëren | Beide partijen delen één secret | Authenticatie voor één service, microservices binnen één team |
| RS256 (RSA) | Asymmetrisch — private key ondertekent, public key verifieert | Afzender houdt private key; iedereen met public key kan verifiëren | OAuth2-providers, third-party API-integraties |
| ES256 (ECDSA) | Asymmetrisch —zelfde model als RSA maar met elliptische krommen | Kleinere keys, snellere verificatie | Mobiele apps, prestatiegevoelige services |

De “alg: none”-aanval
Dit is een van de gevaarlijkste JWT-kwetsbaarheden. Een aanvaller past de header aan om "alg": "none" te claimen en verwijdert de handtekening. Een slecht geïmplementeerde parser zou dit kunnen accepteren en het token als geldig behandelen zonder enige verificatie.
Verdediging: Je parser moet elk token waarvan het algoritme “none” is of niet overeenkomt met je verwachte algoritme expliciet afwijzen. Stas Persiianenko, die de Apify JWT-tool ontwikkelde, benadrukt dat hoewel tokens transparant zijn qua ontwerp, hun beveiliging afhangt van de parser die niet-ondertekende of gemanipuleerde tokens strikt afwijst.
decoded = jwt.decode(token, key, algorithms=None) # NEVER do this
decoded = jwt.decode(token, key, algorithms=["HS256"])
Standaard JWT-claims: wat elk veld betekent
Een JWT-parser haalt “claims” uit de payload. Deze volgen het JOSE (JSON Object Signing and Encryption)-raamwerk voor compatibiliteit tussen systemen.
| Claim | Volledige naam | Doel | Voorbeeldwaarde |
|---|---|---|---|
iss |
Issuer | Wie het token heeft uitgegeven | "auth.example.com" |
sub |
Subject | De gebruiker of entiteit die het token vertegenwoordigt | "user:12345" |
aud |
Audience | Bedoelde ontvanger van het token | "api.example.com" |
exp |
Expiration Time | Wanneer het token ongeldig wordt | 1700000000 (Unix-timestamp) |
iat |
Issued At | Wanneer het token is aangemaakt | 1699999999 |
nbf |
Not Before | Token is niet geldig vóór dit tijdstip | 1699999999 |
jti |
JWT ID | Unieke identificatie voor het token | "a1b2c3d4" |
Bij gebruik van asymmetrische handtekeningen verwijzen parsers vaak naar een JWK (JSON Web Key) — een JSON-structuur die een public key vertegenwoordigt. De parser haalt automatisch de juiste JWK op van de metadata-endpoint van de uitgever om het token te verifiëren.
Implementatie: echte code voor productie
PHP met lcobucci/jwt
De standaard in het PHP-ecosysteem is lcobucci/jwt. Gegevens van Packagist tonen meer dan 322 miljoen installaties vanaf april 2026, wat het de standaardkeuze maakt voor Laravel- en Symfony-projecten.
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Signer\Key\InMemory;
$config = Configuration::forSymmetricSigner(
new Sha256(),
InMemory::plainText('your-secret-key')
);
// Parsing and validating a token
$token = $config->parser()->parse($jwtString);
// Verify constraints: expiration, issuer, etc.
$constraints = [
new \Lcobucci\JWT\Validation\Constraint\IssuedBy('auth.example.com'),
new \Lcobucci\JWT\Validation\Constraint\PermittedFor('api.example.com'),
new \Lcobucci\JWT\Validation\Constraint\SignedWith(
$config->signer(),
$config->signingKey()
),
];
$isValid = $config->validator()->validate($token, ...$constraints);
Hono (Edge/Serverless) met Web Crypto
Voor lichtgewicht edge-applicaties biedt de Hono JWT Helper een minimale decode()-functie, perfect voor serverless platforms waar je snelle cold starts en minimale afhankelijkheden wilt.
import { jwt } from 'hono/jwt'
// Middleware to verify JWT on every request
app.use('/api/*', jwt({ secret: 'your-secret' }))
// Access decoded claims in your handler
app.get('/api/profile', (c) => {
const payload = c.get('jwtPayload')
return c.json({ user: payload.sub })
})
AI-gedreven JWT-analyse met MCP
Tegen 2026 stelt het Model Context Protocol (MCP) AI-assistenten zoals Claude Code of Cursor in staat rechtstreeks met JWT-tools te communiceren. Stel een MCP-server in en een ontwikkelaar kan een AI vragen om “Controleer alle JWT’s in deze logs op verval-fouten” — de agent handelt het parsen af via de command line.
Volgens Apify kost bulkverwerking ongeveer $11.50 per 10.000 tokens vanaf 2026. Deze automatisering stelt AI-agents in staat verlopen tokens te vinden en direct suggesties te doen voor codefixes voor de beveiligingsinstellingen van de app.
Conclusie
Een JWT-parser is meer dan een gemak bij het debuggen — het is een essentiële beveiligingscontrole. Het zorgt ervoor dat tokens authentiek zijn via handtekeningcontroles en geldig via claimverificatie. Onthoud de twee regels die het meest van belang zijn: Base64URL is geen versleuteling, dus bewaar nooit secrets in de payload. En specificeer altijd expliciet toegestane algoritmes om “alg: none”-aanvallen te voorkomen.
Gebruik voor productie-apps bewezen bibliotheken zoals lcobucci/jwt of de JWT-helper van Hono in plaats van je eigen parser te bouwen. Voor debugging en bulkanalyse zijn AI-gedreven MCP-tools de moderne aanpak om beveiligingsaudits geautomatiseerd en grondig te houden.
FAQ
Is het legaal om een JWT-token te decoderen dat ik in mijn browser heb gevonden?
Ja, het is volledig legaal. JWT’s zijn ontworpen om transparant te zijn — de header en payload zijn gecodeerd voor transport, niet versleuteld voor geheimhouding. Het bezitten van het token impliceert dat je toegang hebt tot de gegevens in de claims. Houd echter altijd rekening met lokale privacywetgeving zoals de AVG/GDPR wanneer tokens persoonlijke informatie bevatten.
Waarom toont mijn JWT-parser isExpired: true voor een token dat ik zojuist heb gegenereerd?
Dit wordt meestal veroorzaakt door klokafwijking (clock drift) tussen de server die het token genereerde en het systeem dat het parseert. Als de klokken van de twee systemen niet gesynchroniseerd zijn (via UTC/NTP), kunnen de exp– of nbf-claims ongeldig lijken. Los dit op door ervoor te zorgen dat beide systemen NTP gebruiken voor tijdsynchronisatie, of voeg een kleine “leeway” toe (meestal 60 seconds) in je parse-bibliotheek om kleine afwijkingen op te vangen.
Kan ik een JWT decoderen zonder de secret of public key te hebben?
Ja, je kunt altijd de Header en Payload decoderen en lezen zonder key, omdat ze simpelweg Base64URL-gecodeerde JSON zijn. Je kunt echter de Signature niet verifiëren of erop vertrouwen dat de gegevens authentiek zijn zonder de bijbehorende secret (voor HS256) of public key (voor RS256). Zonder verificatie moet je de gegevens beschouwen als ongeverifieerd en mogelijk gemanipuleerd.
Wat is de “alg: none”-aanval en hoe voorkom ik deze?
De “alg: none”-aanval maakt gebruik van parsers die het in de token-header gespecificeerde algoritme accepteren zonder validatie. Een aanvaller verandert de header in "alg": "none" en verwijdert de handtekening, waardoor een kwetsbare parser wordt misleid het token als geldig te accepteren. Voorkom dit door in je verificatiecode altijd expliciet toegestane algoritmes op te geven — accepteer nooit “none” en laat het token nooit bepalen welk algoritme wordt gebruikt.

Geef een reactie