Tokens & JWTs

A track of P55 · OAuth 2.0 / OIDC & Federated Identity.

What a JWT is and how to verify one correctly — HS256 from scratch, the alg:none and key-confusion attacks, and disciplined exp/aud/iss checks.

Copy a JWT out of a request and paste it into any online decoder. It will show you the claims immediately: the user id, the roles, the expiry, all of it, with no key required. That surprises people the first time, and it should prompt the right question — if anyone can read a token, and the parts are just base64-encoded text, what stops anyone from writing one that says they are an administrator? The answer is a signature, and the entire security of the scheme rests on verifying it in a way that cannot be talked out of.

The track builds this from the inside. You will implement HS256 yourself — the header and payload encoded, an HMAC computed over them with a shared secret, appended as the third part — which makes it concrete that a JWT is signed and not encrypted, and that putting anything confidential in the payload is a mistake regardless of how well you verify it. With a working implementation in hand, you then break your own verifier twice, because both attacks are ones real libraries have shipped.

The first is alg:none. If your verifier reads the token's own header to decide how to check the token, an attacker sets the algorithm to "none", drops the signature, and your code cheerfully accepts a token they wrote. The second is key confusion: a verifier that accepts either RS256 or HS256 based on the header can be handed a token signed with HMAC using the server's public RSA key as the shared secret — a value the attacker can freely obtain. Both share one root cause, which is letting untrusted input decide how it will be validated, and one fix: decide the algorithm and key before you look at the token. The track closes on the checks that are skipped most often — exp, aud and iss — where a token that is perfectly valid for a different audience is accepted by an API that never asked who it was for.

BuildHS256 from scratchalg noneheader decides, attacker winsConfusionpublic key as HMAC secretClaimsexp, aud and iss, every time
Both classic JWT breaks come from letting the token choose its own validation; the fix is deciding first.
Tokens & JWTs — TransformerLab