JWT Attacks
A practical guide for testing JSON Web Tokens (JWT)
Token Discovery
Find where tokens are used in the application:
Authorization: Bearer <token>headersCookie values (
session,token,jwt, etc.)URL parameters
Request bodies
Determine how the token is transmitted and capture a valid, signed JWT.
Token Inspection
Decode the token and inspect all three parts:
header.payload.signatureCheck the header for:
alg— signing algorithm (HS256,RS256,none, etc.)kid— key IDjwk— embedded JSON Web Keyjku— JWK Set URLx5c— X.509 certificate chaincty— content type
Check the payload for sensitive claims:
sub,username,emailrole,isAdminiat,exp,aud,iss
Confirm the signature exists and is non-empty.
The payload is only Base64url-encoded, not encrypted. Anyone can read it.
Signature Verification Bypass
Missing signature verification
Modify a claim such as
sub,username,role, orisAdmin.Leave the original signature unchanged.
Resend the request.
If the server accepts the modified token, the application likely uses
decode()instead ofverify().
Example in Python:
Algorithm none
Decode the header.
Change
algtonone.Modify the payload as desired.
Re-encode the header and payload.
Keep the trailing dot to preserve the
header.payload.structure.
If the string none is blocked, try variants:
None,NONE,nOnEUnexpected whitespace or encoding differences
Expected impact
Account takeover by changing user identifier claims
Privilege escalation by changing
roleorisAdminSession extension or replay by tampering with time claims
Weak Secret Recovery
Use this section when the token is signed with a symmetric algorithm (HS*).
Check whether the secret could be weak, default, or copied from documentation. Then run one of the following tools.
hashcat
Show previously cracked secrets without re-running:
flask-unsign
Useful for Flask sessions that are JWT-like:
If no wordlist works, assess whether a short secret can be brute-forced character by character.
After recovering the secret, re-sign a modified token with HS256 and confirm acceptance.
Header Injection
Embedded JWK (jwk)
Use when the token header contains a jwk parameter.
Generate a new RSA key pair.
Modify the payload, for example change
roletoadministrator.Sign the token with your private key.
Embed your public key in the
jwkheader.Ensure the
kidin the header matches thekidin the embedded JWK.Send the forged token and verify acceptance.
Example in Python:
Burp JWT Editor can automate this via Attack → Embedded JWK.
External JWK Set (jku)
Use when the token header contains a jku parameter.
Host a JWK Set containing your public key on a server you control.
Modify the payload.
Sign the token with your private key.
Set
jkuto your hosted JWK Set URL.Send the forged token.
If
jkuhosts are restricted, test for URL parsing discrepancies, open redirects, or SSRF-like bypasses.
Example JWK Set:
jku SSRF → Localhost Verification Bypass
The jku header can trigger SSRF to localhost-only endpoints. If the server performs side effects during key fetching (such as account verification), point jku to that endpoint.
Chain:
Generate an RSA key pair.
Forge a JWT with
jkupointing to a localhost verification endpoint:
Sign with your private key.
Server validates JWT → fetches
jkuURL → triggers account verification → accepts token.Verified account may unlock privileged endpoints.
Even if external
jkuURLs are blocked, localhost endpoints with side effects may still be reachable.
Key ID (kid) abuse
Use when the server selects keys based on kid.
Check whether
kidis used as a filesystem path.Test path traversal:
Try pointing
kidto/dev/nulland signing with an empty string.
Try predictable file contents such as
../../../proc/sys/kernel/hostname.Test
kidfor SQL injection if it is used in a database lookup.
Other header parameters
Check for
x5cand test self-signed certificate injection.If signature verification is bypassable, test
ctypivots:text/xml→ XXEapplication/x-java-serialized-object→ deserialization
Algorithm Confusion
Use this section when the token uses an asymmetric algorithm such as RS256.
Capture a legitimate JWT and confirm
algisRS256.Obtain the server's public key from a JWK Set, certificate, or metadata endpoint.
Modify the payload as desired.
Change the header
algtoHS256.Sign the token using the server's public key as the HMAC secret.
Send the forged token.
If the server accepts it, algorithm confusion is present.
Example in Node.js:
Preconditions
The server reads the
algheader from the token.The same key material is used for both asymmetric and symmetric verification.
The public key is available to the attacker.
The verification library allows HMAC verification with an RSA public key.
Business Logic Attacks
JWT Balance Replay (MetaShop Pattern)
Use when the token contains mutable state such as a balance, points, or credits.
Sign up and capture the initial JWT with a high balance.
Spend the balance until it reaches zero.
Replace the current cookie with the saved JWT.
Return or refund items and observe whether the server recalculates the balance from the token without cross-checking history.
Repeat until the balance exceeds the target price.
The server trusts the balance in the JWT for return calculations but does not validate purchase history.
JWE Token Forgery
Use when the application uses JWE (JSON Web Encryption) instead of JWT. JWE tokens have five Base64url segments:
If the server's public RSA key is exposed (via /api/key, .well-known/jwks.json, or page source), you can encrypt arbitrary claims that the server will trust after decryption.
Example in Python:
Fetch the exposed public key.
Forge claims.
Encrypt with the public key.
Send the JWE token as cookie or header.
Confirm the server decrypts and accepts the forged claims.
JWE encryption does not equal authentication. If the server trusts any token it can decrypt, exposing the public key allows arbitrary claim forgery.
End-to-End Attack Flows
Account takeover via missing verification
Log in as a low-privilege user and capture the JWT.
Identify the user identifier claim (
sub,username, etc.).Change the claim to a target user.
Keep the original signature.
Send the token and confirm access as the target user.
Privilege escalation via alg: none
Capture a legitimate JWT.
Change
algtonone.Modify the payload to elevate privileges.
Re-encode and append a trailing dot.
Send the token and confirm elevated access.
Secret recovery and re-signing
Capture a valid
HS256JWT.Run hashcat:
Recover the secret.
Modify the payload.
Re-sign with
HS256using the recovered secret.Confirm the server accepts the re-signed token.
Self-signed JWT via jwk
Generate an RSA key pair.
Modify the payload.
Sign with your private key.
Embed your public key in the
jwkheader.Match
kidbetween header and embedded JWK.Send and confirm acceptance.
Self-signed JWT via jku
Generate an RSA key pair.
Host a JWK Set with your public key.
Modify the payload.
Set
jkuto your hosted JWK Set.Sign with your private key.
Send and confirm acceptance.
jku SSRF → localhost verification bypass
Generate an RSA key pair.
Find a localhost endpoint with side effects such as account verification.
Forge a JWT with
jkupointing to that endpoint.Sign with your private key.
Send and confirm the side effect triggers and the token is accepted.
kid path traversal
Capture an
HS256JWT.Set
kidto/dev/nullor another known file path.Modify the payload.
Sign with the file contents as the secret.
Send and confirm acceptance.
RS256 to HS256 confusion
Capture an
RS256JWT.Obtain the server's public key.
Modify the payload.
Change
algtoHS256.Sign with the public key as the HMAC secret.
Send and confirm acceptance.
JWE token forgery
Identify a JWE token (five dot-separated segments).
Locate the exposed public key.
Forge claims.
Encrypt with the public key.
Send and confirm acceptance.
Tools
Burp Suite + JWT Editor
Modify, re-sign, and inject JWTs
Burp Repeater
Replay requests with modified tokens
hashcat
Offline brute-force of HMAC secrets (-m 16500)
flask-unsign
Decode and brute-force Flask session cookies
jwt.io debugger
Manual encoding and decoding
Python jwt / jwcrypto libraries
Scripted token forging and JWE encryption
Python requests
Scripted automation (route through Burp proxy for logging)
Use this guide during web application penetration tests, CTF challenges, code reviews, and secure-design sessions. Attach evidence for each confirmed finding.
Last updated