Hari PrakashTwo JWT algorithm confusion attack CVEs hit in January 2026 — Hono (CVSS 8.2) and HarbourJwt. Learn how to spot forged tokens and pin algorithms in production.
Two JWT algorithm confusion attack CVEs dropped in January 2026, both with public proof-of-concept exploits, both exploiting the exact same root cause: JWT libraries that let the token's own alg header dictate how signature verification works. CVE-2026-22817 hit Hono — one of the fastest-growing edge-runtime frameworks — with a CVSS score of 8.2. CVE-2026-23993 hit HarbourJwt, a Go library, with a bypass so simple it requires zero cryptographic knowledge. If you run anything that validates JWTs, this is your wake-up call to check whether your library actually pins the algorithm.
I spent a morning decoding forged tokens from both POC exploits using the PinusX JWT Decoder, and the signatures of a weaponized JWT are obvious once you know what to look for. Here's the breakdown.
Hono is a lightweight web framework that runs on Cloudflare Workers, Deno, Bun, and Node.js. Its built-in JWT middleware, in all versions before 4.11.4, was vulnerable to a classic JWT algorithm confusion attack: it trusted the alg field in the token header to decide which verification method to use.
The attack works like this. Your server signs tokens with RS256 — RSA private key signs, RSA public key verifies. The public key is, well, public. An attacker grabs it, then crafts a new JWT with two changes:
When the vulnerable Hono middleware receives this token, it reads the alg header, sees HS256, and switches to HMAC verification — using the public key as the HMAC secret. The signature matches. The attacker's forged claims are now trusted.
This is classified as CWE-347 (Improper Verification of Cryptographic Signature) and affects every Hono deployment on every runtime prior to version 4.11.4. The POC exploit is already public, so this isn't theoretical.
Paste the forged token into a JWT decoder and the header immediately gives it away:
// Normal token header from your server
{
"alg": "RS256",
"typ": "JWT"
}
// Forged token header — the red flag
{
"alg": "HS256",
"typ": "JWT"
}
If your server is configured for RS256 but you see HS256 in a decoded token, something is wrong. Either an attacker is probing your system, or your library is silently accepting algorithm swaps.
This one is even simpler. HarbourJwt, a Go JWT library, didn't reject unknown algorithm values. An attacker could set alg to literally anything — "zzz", "foo", "banana" — and the library's GetSignature() function would return an empty byte slice instead of an error. The forged token format is:
eyJ0eXAiOiJKV1QiLCJhbGciOiJ6enoifQ.eyJzdWIiOiIxMjM0NTY3ODkwIiwiYWRtaW4iOnRydWV9.
Notice the trailing dot with nothing after it — that's an empty signature segment. The library compared its computed empty signature against the token's empty signature, they matched, and the token was accepted as valid. No key needed. No cryptography involved at all.
The same security review uncovered a comparable bypass in jose-swift (tracked as GHSA-88q6-jcjg-hvmw), suggesting this pattern isn't limited to obscure libraries.
You don't need security tooling to catch these. Any client-side JWT decoder will show you the header. Here's what to look for:
Drop a suspicious token into the PinusX JWT Decoder and check the header. It runs 100% client-side — the token never leaves your browser — so you can safely inspect production tokens without leaking them to a third-party server.
These two CVEs aren't isolated incidents. Q1 2026 has seen a cluster of JWT algorithm-related vulnerabilities across multiple languages and frameworks:
Go: HarbourJwt (CVE-2026-23993) — unknown algorithm bypass
TypeScript/JavaScript: Hono (CVE-2026-22817) — RS256-to-HS256 confusion, CVSS 8.2
Java: Keycloak (CVE-2026-23552) — accepted cross-realm tokens due to missing iss claim validation
Swift: jose-swift (GHSA-88q6-jcjg-hvmw) — similar unknown algorithm bypass
The pattern is clear: library authors across the ecosystem are still implementing JWT verification in a way that trusts token-supplied metadata. Over 8,000 ChatGPT API keys were simultaneously found exposed in GitHub repos and production JavaScript bundles in February 2026, reinforcing that credential and token hygiene is at a crisis point industry-wide.
Pin the algorithm. Do it today. Here's how in the most common libraries:
// Node.js (jsonwebtoken)
jwt.verify(token, key, { algorithms: ['RS256'] });
// Python (PyJWT)
jwt.decode(token, key, algorithms=['RS256'])
// Go (golang-jwt)
token, err := jwt.Parse(tokenString, keyFunc,
jwt.WithValidMethods([]string{"RS256"}))
// Java (jjwt)
Jwts.parser()
.requireSignedWith(SignatureAlgorithm.RS256)
.setSigningKey(key)
.parseClaimsJws(token);
And the full checklist:
What is a JWT algorithm confusion attack?
A JWT algorithm confusion attack exploits JWT libraries that trust the alg field in the token header to choose the signature verification method. An attacker changes the algorithm — for example, from RS256 (asymmetric) to HS256 (symmetric) — and signs the forged token with the server's public key used as the HMAC secret. If the library doesn't enforce which algorithm is allowed, it verifies the forged signature as valid. The fix is to hardcode the expected algorithm in your verification logic, never letting the token itself dictate how it should be verified.
How do I know if my JWT library is vulnerable to algorithm confusion?
Check whether your verify() call explicitly specifies the allowed algorithm. If you're calling something like jwt.verify(token, key) without an algorithms parameter, your library may be using the token's own alg header — and you're likely vulnerable. Test by crafting a token with a swapped alg value (e.g., HS256 instead of RS256) and submitting it. If your server accepts it, you have a problem. Update to the latest version of your JWT library and always pass an explicit algorithm allowlist.
Can I detect a forged JWT by decoding it?
Yes. The JWT header is unencrypted Base64url-encoded JSON, so decoding it instantly reveals the alg value. If you see HS256 on a system configured for RS256, or an unrecognized value like "zzz" or "none", the token is either forged or indicates a misconfiguration. A client-side JWT decoder like the PinusX JWT Decoder lets you inspect tokens safely without sending them to a third-party server.