const publicKey = fs.readFileSync("public.pem");
const claims = jwt.verify(token, publicKey); // trusts the token's alg
Algorithm confusion; pin the accepted algorithms, e.g.
{ algorithms: ["RS256"] }, so the token's own header never picks the algorithmThis is algorithm confusion. You stop it by pinning the accepted algorithms, for example { algorithms: ["RS256"] }.
The old library trusts the alg value in the token's header. When the header says HS256, the library uses the public key's bytes as an HMAC secret. That key is public, so the attacker can compute a valid HMAC too. Pinning the expected algorithm for each key closes the hole, because the token can no longer choose its own algorithm.
"Key leakage; the public key must be kept secret" is the tempting wrong answer. It misses the real flaw: the token was allowed to choose its own algorithm.
Read more