APIs & Auth

JWT Attacks: The Complete Practical Guide

What JWT attacks are, how to exploit the none algorithm, weak secrets, algorithm confusion, and header injection, and how to prevent them. With free labs.

Published

JWT Attacks: The Complete Practical Guide

A JWT is a little signed packet of claims the server hands you and then trusts when you hand it back. JWT attacks are every way to make the server accept a token it should have rejected: one that is unsigned, one re-signed with a key you were never supposed to have, or one whose headers trick the server into verifying it with the wrong key. The whole security model rests on a single step, checking the signature, so almost every JWT bug is really a verification bug: the server reads your claims but never properly confirms who signed them. Want to feel it rather than read about it? Sign up free and try it on a live target.

What is a JWT?

The mental model in one sentence: a JWT is a token whose contents you can read and edit, but should not be able to re-sign. The server builds it, signs it with a key only the server knows, and sends it to you, usually after login. On every later request you send it back, and the server checks the signature to confirm it is the same token it issued, then trusts the claims inside, like your user id and role.

The attack surface is the gap between "read and edit" and "re-sign." You can always decode a token and change the claims. The only thing stopping you from becoming admin is the signature check. Break, skip, or fool that check and a token you edited becomes a token the server trusts.

Anatomy of a token

A JWT is three base64url-encoded parts joined by dots: header.payload.signature. The header names the algorithm, the payload holds the claims, and the signature is computed over the first two parts with the server's key. You need to be able to read one by eye before you attack it, so start with anatomy of a JWT, which breaks down each part and the dangerous header parameters (kid, jwk, jku, x5u) that later attacks abuse.

Why JWTs get attacked

JWTs are stateless and client-held. The server does not keep a copy, it trusts whatever you return as long as the signature checks out. That design is convenient, and it means the token is only ever as safe as the verification step. Three things make that step go wrong, and they map to the main attack families:

  • The server does not really verify (the none algorithm, or no check at all).
  • The signing key is weak and can be recovered.
  • The server can be fooled about which key to verify with.

Each of those is a spoke below.

Signature verification flaws

The simplest attack: get the server to skip the check. The JWT standard defines an algorithm called none, meaning the token is unsigned. A server that honours a none value in the header will accept a token with any claims and an empty signature, so you just edit your role to admin and drop the signature. Some servers go further and never verify at all, trusting a decoded token on sight. Flawed signature verification covers the none variants and how to spot a server that only decodes.

Weak secrets

When a token is signed with HMAC (HS256) and the secret is a human-chosen string, you can crack it. Capture one valid token, run a wordlist against its signature offline, and if the secret is weak you recover it. Now you hold the signing key and can forge any token you want. Weak secret brute force walks the capture, the crack, and the forge, and why a long random key shuts it down.

Algorithm confusion

A subtler key attack. If a server uses asymmetric RS256 (it verifies with a public key that anyone can have) but will also accept HS256, you can sign an HS256 token using that public key as the HMAC secret. The server verifies your HS256 token with the same public key and it matches, because you both used the identical bytes. Algorithm confusion covers getting the public key and the exact byte-for-byte gotcha that makes or breaks the attack.

Header-parameter injection

Several header parameters tell the server which key to verify with, and trusting them is fatal. jwk lets a token embed its own public key, jku points the server at a JSON Web Key Set you host, kid names a key by id (open to path traversal or SQL injection in the lookup), and x5u/x5c do the same with certificates. JWK, JKU, and KID injection shows how each one lets you nominate a key you control.

Claim tampering

Once you can sign, the claims are yours to rewrite. Change sub or role to escalate, extend exp to keep a token alive, or set nbf so a token is already valid. And you do not always need to sign: plenty of servers decode a token but never check exp, so an old captured token replays just fine. Claim tampering is the payoff of every other attack here, which is why the forge step appears in each spoke.

Working with JWTs in Burp

All of this is faster in a proxy. Working with JWTs in Burp shows how to decode a token in place, edit its claims, re-sign it with a known or cracked key, automate the none and key-confusion attacks, and replay the forged token straight into a request.

A worked example

Say you log in and get a token. You decode it and see {"alg":"HS256"} with {"sub":"wiener","role":"user"}. First you test the cheapest bug: you rebuild the token with the header algorithm set to none, change the role claim to admin, drop the signature to an empty string (keeping the trailing dot), and send it. The app responds as admin. Verification was never enforced, so editing the token was enough to forge it.

If none had been rejected, you would move down the list: try cracking the HS256 secret offline, and if the token were RS256 instead, fetch the public key and try algorithm confusion. The loop is always the same: decode, find the weak point in verification, then forge the claims you need and re-sign with whatever key you now control.

Prevention

  • Pin the algorithm server-side. Decide which algorithm you accept and reject everything else. Never let the token's alg header choose how it is verified.
  • Reject none outright. There is no reason to accept an unsigned token in a normal web session.
  • Verify every signature, and fail closed. Decoding claims is not verification. Use a library call that enforces the signature check and reject the request if it cannot.
  • Use strong keys. For HMAC, a long random secret, never a human-chosen word. For RSA or EC, manage the keys properly and never expose signing material.
  • Do not trust attacker-controlled headers. Ignore or strictly allow-list kid, jku, jwk, x5u, and x5c so a token cannot name its own verifying key.
  • Validate the claims. Check exp, nbf, iss, and aud on every request, keep tokens short-lived, and support revocation.

The full per-library detail is in preventing JWT attacks.

Where it connects

JWT forgery is one of the cleanest routes to broken access control: once you can set your own role claim and have the server trust it, you have escalated privilege directly. The two topics are siblings, a forged token is just an access-control decision made with attacker-controlled input.

Where to go next

Work through the cluster in order, or jump to what you need. Start with anatomy of a JWT if the structure is new, then flawed signature verification and weak secret brute force. When you want every payload in one place, the JWT cheat sheet has them. And when you are ready to break something real, sign up free and start hacking.