Burp Suite’s JWT editor lets you tamper with JSON Web Tokens, but it’s not just for testing your own app’s security; it’s a surprisingly powerful tool for understanding how JWTs are supposed to work.
Let’s say you’ve intercepted a request with a JWT in the Authorization header:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKK92wYy_i2m0rI0I59j4w6X-wQjC0X5c
Right-click that request in Burp’s HTTP history and select "Send to JWT Editor." This opens a dedicated panel where the JWT is parsed into its three base64-encoded parts: header, payload, and signature.
You can edit the header and payload directly. For instance, change "alg": "HS256" to "alg": "none" in the header, and then modify a claim in the payload, say, changing "sub": "1234567890" to "sub": "9999999999". When you click "Update JWT," Burp will re-encode the parts. If you chose "alg": "none", the signature part will be stripped entirely. If you kept an algorithm like HS256, Burp will prompt you for a secret key to re-sign the token.
This interactive process reveals the fundamental structure: a token is just base64-encoded JSON, signed (or not) by a key. The "alg" header tells the receiving server how to verify the signature (or if it even needs to).
Here’s how it works internally:
- Header: Contains metadata, most importantly the signing algorithm (
alg). Common values areHS256(HMAC with SHA-256),RS256(RSA with SHA-256), andnone. - Payload: Contains the claims, which are statements about the entity (usually the user) and additional data. Standard claims include
sub(subject),iss(issuer),aud(audience),exp(expiration time),iat(issued at), etc. Custom claims can also be added. - Signature: Generated by taking the base64-encoded header, concatenating it with a dot (
.), concatenating that with the base64-encoded payload, and then signing the result using the algorithm specified in the header and a secret key. This signature is then base64-url encoded.
When a server receives a JWT, it:
- Decodes the header and payload.
- Reads the
algfrom the header. - If
algisnone, it trusts the payload implicitly (a common vulnerability). - If
algis an HMAC algorithm (likeHS256), it uses the same secret key used for signing to compute a signature over the header and payload and compares it with the received signature. - If
algis an RSA algorithm (likeRS256), it uses the public key corresponding to the private key used for signing to verify the signature.
The JWT editor is invaluable for testing for common misconfigurations. For example, if a server accepts a token signed with HS256 but also accepts a token with alg: "none" (even if the signature is invalid or missing), it means the server isn’t properly checking the alg header. You can also test if the server correctly validates claims like exp (expiration) or aud (audience).
A common attack vector involves tricking a server that uses RS256 into treating a signature generated with a public key as if it were generated with a private key. If the server is configured to accept RS256 but you can control the public key used for verification, you can generate a token where the kid (key ID) header parameter points to a resource you control, or you can forge a token by signing it with your own private key (if you know the server’s public key). Burp’s JWT editor makes it easy to craft these modified tokens.
What most people don’t realize is that JWTs are not encrypted by default; they are only signed. This means anyone can decode the header and payload and read all the information contained within them. If sensitive data is stored in the payload without further encryption (e.g., using JWE - JSON Web Encryption), it’s exposed. The signature only ensures the integrity and authenticity of the claims, not their confidentiality.
Once you’ve successfully bypassed JWT authentication, the next hurdle is often understanding how session management is handled after the token is validated.