DEV Community

Davi
Davi

Posted on Originally published at blog.mago.team

JWT Vulnerabilities: Common Attacks and How to Prevent Them

A request with alg:none in the JWT header bypassed Auth0's authentication in 2019. The same attack surfaced in Hono in 2026. Seven years, same root cause: the library trusted whatever the attacker put in the header.

JWT vulnerabilities persist in production because most libraries trust the algorithm declared in the token by default. Three attack classes, algorithm confusion, header injection, and weak claims validation, consistently bypass authentication even in hardened applications. Automated scanning tools reveal these problems are far more common than security teams assume.

The Header Trust Problem: Why JWT Is Structurally Vulnerable

JWT lets the token declare its own algorithm, which requires the server to actively reject that declaration. Most developers miss this counter-intuitive requirement.

RFC 7519 does not require servers to ignore the header's alg field. The specification defines alg as the algorithm used to sign the token, but does not define what the server must do when the value differs from what it expects. The jsonwebtoken package for Node.js accepted the header algorithm by default before version 4.2.2, meaning any application running earlier versions was vulnerable out of the box.

Most JWT libraries prioritize flexibility over security in their defaults. A developer who follows the standard documentation without reading the security considerations section ships vulnerable code. The validation flow decodes the header, extracts the declared algorithm, and uses that algorithm to verify the signature. When the server does not pin the expected algorithm in the verify() call, the attacker controls which algorithm the server uses to validate the token the attacker created.

The problem is not a specific library flaw. It is a JWT spec design choice that transfers security responsibility to each implementation, with no guarantee that implementations understand that responsibility.

Algorithm Confusion: Three Bypasses That Still Work in 2026

alg:none, RS256-to-HS256 downgrade, and case-insensitive variants of none are three forms of the same root cause. At least one has appeared in a production library every year between 2015 and 2026.

CVE-2015-2951 documented the first large-scale exploitation: multiple libraries accepted alg:none without signature verification. A JWT with alg:none has valid structure but no cryptographic signature. The server, without checking which algorithm it should use, validates the structure and accepts the token. The attacker builds a token with any claim they want (admin: true, sub: another_user, role: superuser) and the server cannot distinguish it from a legitimate token.

CVE-2016-10555 introduced the RS256-to-HS256 downgrade. Systems using RS256 have the public key available, typically via JWKS or a public endpoint. The attacker takes that public key, changes the header alg to HS256, and uses the public key as the HMAC secret to sign a forged token. The server, without checking which algorithm it should use, computes the HMAC using the public key and validates the attacker's token successfully. The public key, by definition exposed, becomes the signing key.

In 2019, Sentor Security researchers found that Auth0 blocked none but not nonE. The blocklist was case-sensitive; the header parser was not. A complete authentication bypass required changing the case of a single letter in the header. CVE-2026-22817 documented the same RS256-to-HS256 pattern in Hono, a modern framework for Cloudflare Workers, confirming that eleven years of public CVEs have not eliminated the root cause.

Header Injection: kid and jwk as Attack Surface

The kid and jwk header parameters, designed to facilitate key rotation, become injection vectors when servers use them without input validation.

kid is a string identifier for the key used in signing. Libraries that build SQL queries to look up the key using the kid value without sanitization accept direct SQL injection. The payload "kid": "x' UNION SELECT 'attacker_secret' --" causes the database to return a value controlled by the attacker, who then uses that value as the secret to sign a new token. When kid acts as a file path, the value ../../../../dev/null resolves to a zero-byte file. An HMAC signed with an empty string combined with that kid validates successfully because the server loads the empty file as the key.

JWK injection bypasses key lookup entirely. Instead of referencing an external key, the attacker embeds a jwk parameter in the token header containing a public key they generated themselves. A server that does not verify the key originates from a trusted JWKS uses the attacker's key to verify the attacker's signature. CVE-2021-46743 revealed that Firebase PHP-JWT allowed kid confusion between different key types within the key ring. HS256 and RS256 keys were resolved by the same lookup mechanism, allowing a token signed with an HMAC key to validate against the RSA key sharing the same kid.

Missing Claims Validation: Tokens That Never Expire and Work Across Services

Tokens without exp, or with unverified aud and iss, remain valid indefinitely or across services. Session hijacking windows persist for weeks after token issuance.

The JWT spec defines exp as an optional claim. Without exp, tokens stolen in a breach remain valid forever. The attacker does not need to refresh or renew the token: a single captured token provides permanent access until the system rotates the signing secret.

Tokens without aud verification created for service A are accepted by service B. In microservice architectures where multiple services share the same signing secret or JWKS, a token issued for a low-risk endpoint authenticates against high-risk endpoints. The attacker obtains a legitimate token for the public query endpoint and uses it to access the administrative endpoint. Ignored nbf allows using tokens issued before their intended activation window.

The OWASP API Security Top 10 2023 lists inadequate token validation as a core risk. Most JWT tutorials focus on signature verification and omit that claims validation is separate and equally critical.

Weak Secrets: Cracking HMAC Offline

JWTs signed with HMAC and secrets shorter than 256 bits crack with jwt_tool and hashcat in minutes, without touching the target server.

jwt_tool has a dictionary attack mode that tests secrets against captured tokens entirely offline. The attacker only needs one valid token, captured from an application error log, API response, or intercepted traffic. CVE-2019-20933 documented libraries that accepted an empty string as a valid HMAC secret. CVE-2020-28637 confirmed the same pattern in a separate library: any token signed with an empty secret passes validation without error.

The Rockyou2024 wordlist, with 10 billion entries, makes short secrets trivially crackable offline. Secrets derived from strings like secret, jwt_secret, change_me, the application name, or any dictionary word appear consistently in scan results. jwt_tool with the --crack flag tests the full wordlist in seconds against the captured token, generating zero logs on the target server.

Real Incidents: CVEs in Production Auth Systems

Auth0, Firebase, Authlib, and Hono all shipped exploitable JWT validation in production, collectively affecting millions of applications.

CVE-2018-6873 disclosed an authentication bypass in Auth0 via JWT parameter manipulation. The 2022 trifecta escalated the problem: CVE-2022-23539 documented a weak signing algorithm in the jsonwebtoken library maintained by Auth0; CVE-2022-23540 covered algorithm verification bypass allowing alg:none; CVE-2022-23541 documented improper authorization where object and false values passed subject verification.

CVE-2021-46743 in Firebase PHP-JWT used kid confusion between key types to make the server validate tokens with the wrong key. CVE-2024-37568 fixed algorithm confusion in Authlib version 1.3.1 in 2024, nine years after CVE-2015-2951. CVE-2026-22817 confirmed the RS256-to-HS256 pattern in Hono in 2026. The frequency is not coincidence: JWT delegates critical security decisions to implementations that follow only the basic documentation.

Automated Detection: What Scanners Find

jwt_tool, PortSwigger's JWT Editor, and jwt_scanner at intel.mago.team detect different attack classes. Used together, they cover the full attack surface without manual token manipulation.

jwt_tool tests alg:none, RS/HS confusion, kid injection, dictionary attacks against weak HMAC secrets, tamper-and-replay, and expired token replay. PortSwigger's JWT Editor integrates with Burp Suite for intercepting and modifying tokens in a pentest workflow, covering JWK and JKU injection against live requests. jwt_scanner at intel.mago.team/spells/jwt_scanner automates API-based detection of algorithm confusion, missing claims, and weak secrets against staging endpoints, without requiring manual proxy configuration.

The gap automated scanners do not cover is application-specific business logic: tokens with correct claims but excessive scopes, or valid tokens reused in flows that require reauthentication, require manual review of each endpoint.

Remediation: Five Controls That Close the Attack Surface

Pinning the algorithm on the server, validating all claims, using RS256 with JWKS, rotating keys, and running scans in CI/CD closes all seven attack classes covered in this post.

Always pass the expected algorithm explicitly to verify(). In Node.js: jwt.verify(token, secret, { algorithms: ['RS256'] }). Never omit the algorithms parameter: its absence is not neutral, it is a vulnerability. Validate exp, nbf, aud, and iss explicitly. Reject tokens that do not contain any required claim rather than treating them as optional.

Prefer RS256 over HS256 for distributed systems: asymmetric keys allow sharing the public key across services without compromising the private signing key. Configure a JWKS endpoint for key distribution with rotation by kid. Run jwt_tool or jwt_scanner against staging tokens in the CI/CD pipeline before each release.

JWT vulnerabilities are a solved problem on paper. Most teams validate the signature but not the algorithm, the claims, or the key origin. Scan your tokens before attackers do.

Top comments (0)