JWT Attacks in 2026: alg:none, RS256-to-HS256, JWKS Injection — Still Working

Manish Garg
Manish Garg Associate of (ISC)² · RingSafe
Apr 25, 2026
6 min read

Last updated: April 29, 2026

JSON Web Tokens are the de-facto authentication primitive for modern APIs. Most JWT implementations are correct. The ones that are not have systematic, well-known weaknesses — algorithm confusion, weak signing keys, key confusion, kid injection — that turn the entire authentication layer into trivial spoofing. This article covers the attack surface, the modern variants, the detection that catches exploitation, and the implementation rules that close every category.

Why JWT bugs persist

JWT was specified loosely. The header includes the algorithm, which the verifier reads — meaning the server can be told which algorithm to use to verify the token. Combined with libraries that historically supported “none” as an algorithm, this gave us the original “alg: none” bypass. That specific bug is mostly fixed in modern libraries, but the pattern continues:

  • RS256 vs HS256 confusion still works in poorly written verifiers
  • JWKS endpoints with weak access controls let attackers inject their own keys
  • Weak HMAC secrets remain crackable offline
  • kid (Key ID) headers without sanitisation lead to SQL/path injection in some implementations

The attacks that still work

1. alg: none

Set the alg header to none, omit the signature, hope the verifier accepts:

{
  "alg": "none",
  "typ": "JWT"
}.{
  "sub": "admin",
  "exp": 99999999999
}.

Defunct in modern libraries (jsonwebtoken v9+, pyjwt v2+). Still works on hand-rolled or legacy implementations. Always test it.

2. RS256 to HS256 confusion

The classic. RS256 uses a public/private key pair; HS256 uses a shared secret. If the server is configured for RS256 and the verifier accepts the algorithm from the header, you can:

  1. Take the server’s public key (often exposed at /.well-known/jwks.json)
  2. Forge a token with alg: HS256 in the header
  3. Sign it using the public key as the HMAC secret
  4. The server, expecting RS256 to verify with its public key, instead uses it as an HMAC secret because the header said HS256
  5. Validation succeeds

Tools: jwt_tool, Burp’s JWT Editor extension automate this.

3. Weak HMAC secret

HS256 uses a shared secret. If the secret is short or guessable (we routinely find secret, jwt-secret, changeme, environment names plus a number), it is crackable offline:

hashcat -a 0 -m 16500 token.txt rockyou.txt

Mode 16500 is JWT HMAC. Cracks in seconds for weak secrets.

4. JWKS injection

Newer pattern. The kid (Key ID) header points to a key. If the server fetches the key from a URL embedded in the token (the jku header), the attacker can host their own JWKS endpoint and sign with the corresponding private key.

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "1",
  "jku": "https://attacker.com/jwks.json"
}

The server fetches https://attacker.com/jwks.json, finds key with kid: 1, verifies the signature against it. Verification succeeds because the attacker controls the key.

Mitigation: validate the jku URL against an allow-list, or never trust jku from the token.

5. kid injection (path traversal / SQLi)

Some servers use the kid value to look up the signing key — from a file path, a database query, or a command. If kid is concatenated unsanitised:

{ "kid": "../../../dev/null" }
// Server reads /dev/null as the key, which is empty.
// Attacker signs with empty string. Verifies.
{ "kid": "x' UNION SELECT 'attacker_secret' -- " }
// SQL injection in kid lookup; returns attacker-controlled secret.

6. Algorithm downgrade

Force the server to a weaker algorithm. ES256 down to HS256, etc. Effective when the server’s algorithm-validation logic is permissive.

7. Token signature stripping

Send the token without a signature, or with the signature replaced by garbage. If the server logs the token but does not enforce signature validation in some code path, exploitation follows.

How to test JWT systematically

  1. Capture a valid token. Login as a regular user, capture the JWT.
  2. Decode it. Burp’s JWT Editor or jwt-cli.
  3. Modify claims and resend. Change sub to admin, change role to admin. If accepted, signature is not being validated.
  4. Test alg: none. Strip signature, set alg: none.
  5. Test RS256-to-HS256. Find the public key (JWKS endpoint), forge with HS256 using the public key.
  6. Crack HMAC secret offline. Use hashcat with rockyou + best64 rules.
  7. Test jku / x5u injection by adding the headers and pointing at attacker-controlled JWKS.
  8. Test kid injection with path traversal and SQL injection payloads.

Implementation rules that close every variant

  • Hardcode the expected algorithm. Do not read alg from the token header. Tell the verifier explicitly: “expect RS256.” Most modern libraries support this; use it.
  • Use a strong signing secret. For HS256, at least 256 bits of cryptographic randomness. Generate via openssl rand -hex 32 or equivalent. Store in a secret manager.
  • Disable the none algorithm at the library level even though modern defaults block it. Defence in depth.
  • Reject jku, jwk, x5u, x5c headers from the client unless your protocol specifically requires them and you allow-list URLs.
  • Sanitise kid against expected key identifiers. Never use it as a path or SQL parameter.
  • Short token lifetime. Access tokens 5-15 minutes. Refresh tokens with rotation.
  • Token revocation — maintain a revocation list (Redis with TTL, or short-lifetime + opaque session ID).
  • Validate iss, aud, nbf, exp claims explicitly on every verification.

Detection — what works at runtime

  • WAF rules for malformed JWTs in Authorization headers — empty signatures, alg: none header values.
  • Application logs for token validation failures. Sustained failures from a single IP indicate enumeration / cracking attempts.
  • Outlier detection on token usage — same token from multiple IPs in different geographies within seconds is anomalous.
  • JWT inspection at the gateway (API Gateway, Kong, Apigee) — log every token’s claims, alert on impossible claim combinations.

How to find your next JWT bug

For attackers:

  • Test every API that uses bearer tokens — not just the obvious user-facing app, but admin APIs, microservice-to-microservice tokens, OAuth resource servers.
  • Look for JWKS endpoints exposed publicly. If yes, RS256-to-HS256 is your first test.
  • Burp Pro’s JWT Editor + jwt_tool together cover ~95% of JWT attack vectors.
  • For mobile-app APIs, decompile the app and look for hard-coded HMAC secrets — surprisingly common.

For defenders:

  • Audit every JWT consumer in your codebase. For each, what library, what algorithm enforcement, what secret source?
  • Run jwt_tool against your own production tokens with various tampers and confirm verification fails on all.
  • Migrate from HS256 to RS256 for any token that crosses trust boundaries.

Compliance angle

  • OWASP API Security Top 10 — broken authentication is the #1 category.
  • DPDP §8(5) — JWT bypass that lets attackers access personal data is a reasonable-security failure.
  • RBI / SEBI — authentication weaknesses in customer-facing APIs are reportable findings.

The takeaway

JWT done right is fine. JWT done with library defaults from 2017 is a privilege-escalation primitive. The implementation rules above are not novel; they have been published by every JWT library maintainer. Apply them to your next API design review. Test your existing implementations with jwt_tool before someone else does. JWT is one of those technologies where the difference between secure and broken is small in lines of code and large in business consequence.

Try it: hands-on challenge

Need a real pentest?

Get a VAPT scoping call

Senior practitioner-led VAPT — not a checklist run by juniors. CVSS-scored findings, free retest, attestation letter. India's SMBs and SaaS teams.

Book VAPT scoping call Replies in 4 working hrs · India-only · Senior consultants