SecLab
Authentication & SessionwalkthroughDifficulty 3/528 min

Verifying a JWT properly

Objective: After this lesson you can list the five things a JWT check must verify, and know why a valid signature is only the first.

V9API2:2023A07:2025RFC 9700jwt
Step 1 of 5 · read6 min

A valid signature does not mean the token is for you

The question a JWT check must answer is not "was this token modified" but "was this issued by somebody I trust, for my system, for this action, and is it still valid". The signature answers only the first.

Five things to check, and the last four are the ones usually skipped:

CheckClaimIf you skip it
Signature, with the algorithm you expectheader algYou accept alg: none, or get algorithm-confused
IssuerissAnother tenant's token, or any issuer's
AudienceaudA token issued for a different service works here
Expiryexp, nbfTokens live forever
Authorisation for this actionscope, rolesA read token works for a write

Two classic holes, both from trusting the token's own header:

(1) alg: none. JWT permits alg: none, meaning no signature. A library verifying "with whatever alg the header says" sees none, concludes nothing needs checking, and returns success. Fix: always state the algorithm you expect; never let the token declare it.

(2) Algorithm confusion. If the server accepts both RS256 and HS256, an attacker takes your public key — which is public — and signs a token with HS256 using that public key as the secret. The server sees alg: HS256, uses the RSA key as an HMAC secret, and the signature matches.

One point worth stating because it is counter-intuitive: the kid header is not trustworthy either. It names which key in your JWKS to use, and a kid pointing at a file path or an attacker-controlled URL has been a real vulnerability class. kid is for looking up within a key set you already have, not for loading a new key.

And one thing about JWTs generally: there is no revocation. An issued token is valid until exp, regardless of a password change, a deleted account, or a removed permission. That is not a bug to fix but a property to design around — which session-lifetime does.

View path

Comments

Join the discussion
Sign up to comment

Commenting needs an account with at least one completed lesson. That condition is what keeps this thread worth reading: every point belongs to someone who can be asked back, and reputation accrues over time.

Sign upSign in

You can still read every comment below without an account. Signing in brings you back to this exact spot, not to the top of the page.

Loading comments…