Feb 20, 2026·6 min read·6 visits
Hono < 4.11.4 trusts the JWT header 'alg' field when the JWK Set doesn't explicitly define one. Attackers can sign malicious tokens using the victim's public key as an HMAC secret (HS256), forging valid sessions.
A critical vulnerability in the Hono web framework's JWK middleware allows attackers to perform a classic JWT Algorithm Confusion attack. By manipulating the JWT header, an attacker can trick the server into verifying an asymmetric signature (RS256) as a symmetric HMAC (HS256) using the server's public key as the shared secret. This results in full authentication bypass and privilege escalation.
In the world of web standards, flexibility is often the enemy of security. Hono, a blisteringly fast web framework designed for the Edge (Cloudflare Workers, Deno, Bun), usually gets things right. But in the implementation of its JSON Web Key (JWK) middleware, it fell victim to a vulnerability class that has been haunting JWT libraries since 2015: Algorithm Confusion.
Here is the setup: You want to verify JWTs signed by an OIDC provider (like Auth0 or Google). You configure Hono to fetch the provider's public keys (JWKS). Everything looks secure because you are validating cryptographic signatures. However, Hono's middleware made a fatal assumption: it assumed that if the key didn't tell you how to use it, the token would.
This is the digital equivalent of a bank teller verifying a check not by looking at the bank's security features, but by asking the robber, "Does this look real to you?" If the robber says yes (and specifies the verification method), the teller proceeds. CVE-2026-22818 is exactly this: a mechanism where the attacker dictates the rules of engagement, turning the server's own public keys against it.
To understand this exploit, you have to understand the difference between Asymmetric (RSA/EC) and Symmetric (HMAC) signatures. In a normal RSA flow (RS256), the server uses a Public Key to verify a signature created by a Private Key. In an HMAC flow (HS256), both parties share a single Secret to sign and verify.
The vulnerability exists in src/middleware/jwk/index.ts. When Hono fetches a JWK from a remote endpoint, that key object might look like this: {"kty": "RSA", "kid": "123", "n": "...", "e": "AQAB"}. Notice something missing? The alg field (e.g., "alg": "RS256") is optional in RFC 7517. If the OIDC provider omits it, Hono has to guess which algorithm to use.
Before version 4.11.4, Hono's logic was roughly: "If the Key doesn't specify an algorithm, use the one listed in the JWT header." This is catastrophic. An attacker can change the JWT header to {"alg": "HS256"}. The middleware sees the header, looks up the key, finds no specific algorithm bound to the key, and agrees to verify the token using HS256. The "secret" used for this HMAC verification becomes the Public Key string itself. Since the Public Key is, by definition, public, the attacker has everything they need to forge a signature.
Let's dissect the vulnerable code in src/utils/jwt/jwt.ts. The logic flow was a textbook example of fallback insecurity. The verify function prioritized the header's claim over strict configuration.
The Vulnerable Logic:
// Pseudocode representation of the flaw
const verifyWithJwks = async (token, jwks) => {
const header = decodeHeader(token);
const key = findKey(jwks, header.kid);
// CRITICAL FAIL: Fallback to untrusted header
const algorithm = key.alg || header.alg;
return await verify(token, key, algorithm);
};Because key.alg is often undefined in wild JWKS implementations, the code defaults to header.alg. If the header says HS256, the verify function uses the PEM representation of key as the HMAC secret.
The Fix (Commit 190f6e2): The patch introduces a breaking change that forces the developer to explicitly allow-list algorithms. It also strictly validates that the header matches the allowed list.
// The patched approach
if (!allowedAlgorithms.includes(header.alg)) {
throw new Error('Invalid algorithm');
}
// Explicitly reject symmetric algs in JWK context
if (header.alg.startsWith('HS')) {
throw new Error('Symmetric algorithm not allowed');
}By enforcing an allowlist and banning HS* algorithms within the JWK context, Hono ensures that a public key is never reinterpreted as a shared secret.
Re-exploiting this requires zero access to the internal network; you just need to know the target is using Hono and where they get their keys (usually a public .well-known/jwks.json endpoint).
Step 1: Reconnaissance
The attacker grabs the JWKS from the target's provider (e.g., https://example.com/.well-known/jwks.json). They extract the RSA public key components (n and e).
Step 2: Key Conversion The attacker converts this JWK into a PEM string. This string is what the server uses as the "key" object. In Node.js/WebCrypto, when you pass a public key object to an HMAC verify function, it simply serializes the key structure.
Step 3: The Forgery The attacker constructs a JWT:
{"alg": "HS256", "typ": "JWT", "kid": "<valid_kid_from_jwks>"}{"sub": "attacker", "role": "admin", "iss": "https://trusted.provider"}Now, the magic trick: The attacker signs this token using HS256. For the secret key, they use the Public Key PEM they extracted in Step 1.
Step 4: Delivery
The attacker sends Authorization: Bearer <forged_token>. Hono reads the header (HS256), fetches the correct public key (via kid), and attempts to verify the signature as an HMAC using that public key. Since the attacker signed it with the exact same data, the math checks out. The server grants admin access.
This vulnerability scores an 8.2 (High) for a reason. While it requires a specific configuration (JWKs without explicit alg fields), that configuration is extremely common. Many OIDC providers do not include the alg field in their JWKS to maintain flexibility during key rotation.
The impact is a complete bypass of authentication. If your application relies on the JWT for authorization (e.g., checking scope or role claims), the attacker can grant themselves any permission they desire. They become the superuser.
Furthermore, because this is an implementation flaw in the verification logic, it affects any Hono-based application exposed to the internet, regardless of whether it's running on AWS Lambda, Cloudflare Workers, or a standard Node.js container. It is a logic bug, not a memory corruption bug, making it reliable and stable to exploit.
The remediation is straightforward but requires code changes due to the breaking nature of the fix in version 4.11.4.
1. Update Hono
Upgrade your dependency immediately:
npm install hono@latest (Ensure version is >= 4.11.4).
2. Update Middleware Config
The jwk() middleware now requires you to specify which algorithms you accept. You can no longer rely on auto-detection.
import { jwk } from 'hono/jwk'
app.use('/protected/*', jwk({
jwks_uri: 'https://example.auth0.com/.well-known/jwks.json',
alg: 'RS256' // YOU MUST SET THIS NOW
}))3. Audit Logs
Check your access logs for JWTs using HS256 (HMAC) on routes that should be using RS256 (RSA). If you see HS256 headers coming into your OIDC-protected endpoints, you have likely been probed or compromised.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
hono honojs | < 4.11.4 | 4.11.4 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-347 |
| Attack Vector | Network |
| CVSS Score | 8.2 (High) |
| EPSS Score | 0.00017 |
| Vulnerability | JWT Algorithm Confusion |
| Exploit Status | PoC Available |
Improper Verification of Cryptographic Signature