JWT Security in 2026: Common Vulnerabilities and How to Test for Them

JSON Web Tokens (JWTs) are the default authentication mechanism for modern APIs. They're stateless, scalable, and well-supported by every framework. They're also frequently misconfigured in ways that let attackers forge tokens, escalate privileges, or bypass authentication entirely.

Here are the vulnerabilities that matter, how to test for them, and how to implement JWTs correctly.

JWT Anatomy (30-Second Refresher)

A JWT has three parts, separated by dots: header.payload.signature

The header and payload are Base64URL-encoded (not encrypted — anyone can read them). The signature is what provides integrity. If you can forge the signature, you can forge the token.

Vulnerability 1: The "none" Algorithm

What it is: The JWT spec includes an "alg": "none" option that means "no signature required." If the server accepts tokens with alg: none, an attacker can create any token they want without knowing the secret.

How to test:

  1. Decode the JWT header
  2. Change "alg" to "none" (also try "None", "NONE", "nOnE")
  3. Remove the signature (everything after the second dot)
  4. Send the modified token
  5. If the server accepts it, the application is vulnerable

Fix: Never accept alg: none. Whitelist allowed algorithms explicitly in your JWT library configuration.

Vulnerability 2: Algorithm Confusion (RS256 → HS256)

What it is: RS256 uses asymmetric cryptography (public/private key pair). HS256 uses symmetric cryptography (shared secret). If the server uses RS256 but also accepts HS256, an attacker can:

  1. Get the server's public key (often available at /.well-known/jwks.json)
  2. Change the token's alg from RS256 to HS256
  3. Sign the token using the public key as the HS256 secret
  4. The server verifies the HS256 signature using the public key — and it matches

This works because the server uses the same key for verification regardless of algorithm. With RS256, it uses the public key to verify. With HS256, it uses the "secret" to verify — and the attacker set the "secret" to be the public key.

How to test:

  1. Fetch the public key from /.well-known/jwks.json or the OIDC discovery endpoint
  2. Change the token header to "alg": "HS256"
  3. Sign the modified token using the public key as the HMAC secret
  4. Send the token and check if it's accepted

Tool: jwt_tool automates this attack with the -X k flag.

Fix: Whitelist the expected algorithm. Never let the token's alg header determine which algorithm the server uses for verification.

Vulnerability 3: Weak Signing Secrets

What it is: HS256 tokens are signed with a shared secret. If the secret is weak (short, dictionary word, default value), it can be brute-forced.

Common weak secrets: secret, password, jwt_secret, changeme, the application name, empty string.

How to test:

  1. Capture a valid JWT
  2. Use hashcat or jwt_tool to brute-force the secret
  3. Hashcat mode 16500 handles JWT cracking: hashcat -m 16500 jwt.txt wordlist.txt
  4. If the secret is found, you can forge any token

Fix: Use a cryptographically random secret of at least 256 bits (32 bytes). Better yet, use RS256 with a proper key pair — there's no shared secret to brute-force.

Vulnerability 4: Missing Claim Validation

What it is: The server verifies the signature but doesn't validate the claims inside the token. An attacker can modify claims (user ID, role, permissions) and re-sign the token (if they know the secret or can exploit algorithm confusion).

How to test:

Also check:

Fix: Validate all claims server-side. Don't trust the token's claims for authorization decisions without verification against the database.

Vulnerability 5: No Expiration or Long Expiration

What it is: Tokens without an exp (expiration) claim are valid forever. Tokens with very long expiration (days, weeks) remain valid long after the user's session should have ended.

How to test:

Fix: Always set exp. Access tokens should expire in 15-60 minutes. Use refresh tokens (with longer expiration) to get new access tokens. Implement token revocation for logout and password changes.

Vulnerability 6: JWK Injection

What it is: Some JWT libraries allow the token header to include a jwk (JSON Web Key) parameter containing the public key to use for verification. An attacker can generate their own key pair, sign the token with their private key, and embed their public key in the token header. The server uses the attacker's public key to verify — and it passes.

How to test:

  1. Generate a new RSA key pair
  2. Add the public key to the JWT header as a jwk parameter
  3. Sign the token with your private key
  4. Send the token

Tool: jwt_tool -X i automates JWK injection.

Fix: Never use the jwk header parameter for verification. Always use a server-side key store.

Testing Workflow

Here's the order to test JWT security:

  1. Decode: Paste the token into jwt.io to understand its structure
  2. Check expiration: Is exp present? Is it reasonable?
  3. Test none algorithm: Quick check, high impact if vulnerable
  4. Test algorithm confusion: If the server uses RS256, try HS256 with the public key
  5. Brute-force secret: If HS256, try common secrets with hashcat
  6. Modify claims: If you can forge tokens, test privilege escalation
  7. Test revocation: Change password, logout — is the old token still valid?

Primary tool: jwt_tool by ticarpi — handles all of the above attacks with a single tool.

Secure Implementation Checklist

## JWT Security Checklist

- [ ] Algorithm whitelisted (never accept from token header)
- [ ] "none" algorithm rejected
- [ ] Secret is cryptographically random, ≥256 bits
- [ ] RS256 preferred over HS256 for public-facing APIs
- [ ] exp claim always set (15-60 min for access tokens)
- [ ] iss and aud claims validated
- [ ] sub claim verified against database
- [ ] Token revocation implemented (logout, password change)
- [ ] Refresh token rotation enabled
- [ ] JWK header parameter ignored
- [ ] Tokens stored in httpOnly cookies (not localStorage)

Bottom Line

JWT vulnerabilities are some of the highest-impact findings in API security testing. A single algorithm confusion or weak secret can give an attacker full admin access. The fixes are straightforward — whitelist algorithms, use strong secrets, validate claims, enforce expiration. Most JWT libraries support all of these out of the box; the vulnerabilities come from misconfiguration, not missing features.

For the full API security testing workflow, see our 10 API security checks. For the broader testing methodology, see the complete security testing checklist.

Advertisement