← JWT Verification Misuse Check

JWT verification misuse: decode vs verify, alg: none and how to fix each

A JSON Web Token is only worth trusting if your server proved that someone holding the key signed it. Most JWT vulnerabilities in application code are not clever cryptography attacks. They are a line that reads the token without ever checking that proof.

// reads the claims, checks nothing: anyone can write { "admin": true }
const claims = jwt.decode(token);

// proves the signature, and the expiry, with a pinned algorithm
const claims = jwt.verify(token, process.env.JWT_SECRET, { algorithms: ['HS256'] });

What is the difference between jwt.decode and jwt.verify?

jwt.decode base64-decodes the token and returns its payload without checking the signature, so anyone can write any claims. jwt.verify checks the signature (and by default the expiry) with your key and throws if the token is not genuine. Use verify for any claim you act on. Decode is fine only for claims you never trust, such as showing a name in a UI.

The nine patterns a code scan can find

What is the JWT alg none attack?

A JWT header names its own algorithm. If a verifier honours alg: none, a token with no signature at all passes. A related trick, algorithm confusion, hands an RS256 public key to a verifier that then treats it as an HS256 secret. Both end the same way: pin the algorithms you accept, never include none, and never let the token choose.

Check your own code

Paste your source into JWT Verification Misuse Check to see every finding with its file, line, risk and fix, or run the CLI in your own repository:

python3 check.py --fail-on-risk

That exits with a nonzero status if any pattern is found, so you can wire it into your own CI. Expect some findings to be deliberate (decoding a token to display a name); review each one.