Hello list, jwt_verify's key argument is documented as holding "a secret or a public key path." The code never separates the two uses. The verification algorithm comes solely from the converter's algorithm argument; on the HMAC branch, the key argument is passed verbatim to HMAC() as the secret. The one guard — ctx->is_x509, src/jwt.c:271 — refuses HMAC only when the key string names a certificate. A bare public-key path has no such protection.
The third component is the documentation's own example, which extracts the algorithm from the token being verified and passes a public-key path as the key. A deployment following the vendor's example has arranged the RS256→HS256 confusion itself — and the resulting HMAC secret is the key's filesystem path, a string that appears in configurations and error messages and is secret from no one. Proven live on the official haproxy:3.4.7 image, configured with the documented pattern: legitimate RS256 token -> accepted forged HS256, key = "/usr/local/etc/haproxy/rsa-pub.pem" (literal) -> accepted (bypass) forged HS256, wrong key -> rejected no token -> rejected 3.4.7 is the current stable release, and master HEAD carries the identical construction. Credit where due: 3.4.7 hardened this same converter against the "none" algorithm, per RFC 8725. The other door of the same class stayed open. No CVE, vendor advisory, or public writeup covers this construction; the search behind that statement is documented in §5 of the paper. The fix has the shape of the guard that already exists: bind the algorithm family to the key type, and refuse HMAC verification for material loaded as an asymmetric key. Paper: https://archive.org/details/knell-the-path-is-the-key Mirror: https://github.com/the-knell/The-Path-Is-the-Key Key: 133883AB9F6EDE5B9BA6B82E27BEDAF0CE152F25 (knell) Verify: gpg --import knell-public.asc, then gpg --verify paper-haproxy-FINAL-2026-11-05.md.asc Any response from the HAProxy developers will be appended to the paper unedited. — knell

