Lesson 8 · Reading · 35 min
JWT and forging identity tokens
There are two ways a server can remember who you are. It can issue an opaque id and look you up in its own store, the session from the last lesson. Or it can hand you a self-contained token that carries your identity, signed so it cannot be tampered with, and trust the token on each request without looking anything up. The most common such token is the JWT, and when a server verifies it carelessly, the token itself becomes the way in.
What a JWT actually is
A JSON Web Token is three base64url pieces joined by dots: header.payload.signature.
- The header says how it is signed:
{"alg":"HS256","typ":"JWT"}. - The payload is the claims, the data the server trusts:
{"sub":"bob","role":"user","exp":...}. - The signature is a keyed hash of the header and payload, and it is the only thing standing between you and forgery.
The single most important fact for a tester: the payload is base64, not encryption. Anyone holding the token can decode and read it, paste it into any JWT decoder and see your own role, user, email. The token is transparent. What you cannot do, if the server checks the signature properly, is change a claim, because changing the payload breaks the signature. Every JWT attack is a way to get a server to accept a token whose payload you changed.
The classic forgeries
alg: none. The JWT spec includes a "none" algorithm meaning "unsigned". If a server honours it, you strip the signature entirely, set the header's alg to none, change the payload's role to admin, and send header.payload. with an empty signature. A server that trusts alg:none accepts it, because you told it there is nothing to verify. Try it whenever you find a JWT: decode it, change a claim, set alg to none, drop the signature.
HS256/RS256 key confusion. Some tokens are signed with RSA (RS256): the server verifies with a public key, which is, by definition, public. The attack: change the header to say HS256 (a symmetric algorithm), then sign your tampered token using the server's public key as the HMAC secret. A server that does not pin the algorithm will verify your HS256 token using that same public key as the secret, and it matches, because you used the very key it verifies with. You have signed a valid token without ever knowing a private secret.
Weak signing secret. If the token is HS256, its security rests on a secret string. If that secret is weak, secret, password, the app name, a short word, you can crack it offline (tools like hashcat, or jwt_tool, brute-force the secret against a captured token). Once you have the secret, you can mint any token you like: any user, any role. Always test a captured HS256 token against a wordlist of common secrets.
Other tampering to try: claims the server over-trusts (isAdmin, role, user_id: change them and see), a missing or ignored exp (does an expired token still work?), and the kid (key id) header, which has been abused for injection when the server uses it to look up a key.
The method, concretely
- Find the token. It is usually in an
Authorization: Bearer ...header or a cookie. Three dot-separated base64 chunks give it away. - Decode it. Read the header (
alg) and the payload (your claims). Now you know what to attack and what to change. - Pick the forgery based on
alg:nonestrip, RS256→HS256 confusion, or crack a weak HS256 secret. - Change a claim that matters,
role: admin, another user'ssub. - Re-sign (or don't) and send it, then see whether the server treats you as the changed identity. Tools like
jwt_toolautomate every step above.
The honesty gate
The finding is getting the server to accept a token you forged, privilege escalation or impersonation. Prove it on your own account: take your token, change your role to admin or your sub to your second test account, and demonstrate the server honours it. A decoded token showing readable claims is not itself a vulnerability, it is how JWTs work. The bug is the server accepting a tampered one. Do not forge tokens for real users' identities on a live system; escalate your own test account and stop at the proof.
Your labs
Three labs cover the family. VaultAPI tokens (JWT) is the general case: a JWT-protected API where the token can be tampered into a privileged one. TicketFlow alg-none forgery is the alg:none bug specifically, strip the signature, claim admin, and be believed. SkyView RS256/HS256 key confusion is the public-key-as-secret trick. Do them in that order: general tampering, then the two named algorithm attacks.
You have finished the access path
Look at what you can now do. You can tell authentication from authorization and know which question a bug breaks. You can hold two accounts and prove one reaches the other's data. You can find objects the server hands over without checking ownership, admin functions hidden only in the UI, weak and default logins, broken resets, hijackable sessions, and forgeable tokens. This is the bug class most bounties and most exam points come from, and you have the whole shape of it. The next path, Injection, turns from who you are allowed to be to what you can make the server's own code do with your input.
This lesson has 4 labs to try what you just read.