The signature is only as good as the verifier
A JWT's signature makes its claims trustworthy, but only if the code checking it does so correctly. Most JWT vulnerabilities are not breaks in the cryptography; they are verifiers that can be tricked into accepting tokens they should reject. Here are the ones that matter, and the checks that prevent them.
Trusting the token's own algorithm
alg: none. The JWT spec defines an "unsecured" token with no signature, declared by{"alg":"none"}. A verifier that honors this will accept a token with forged claims and an empty signature. A correct verifier never treatsnoneas valid for a token that is supposed to be signed.- Algorithm confusion (RS256 to HS256). With RS256 the token is signed by a private key and verified with the public key. If a verifier reads the algorithm from the token and an attacker changes the header to HS256, the verifier may try to verify an HMAC using the public key as the secret, a value the attacker also has. The forged token then passes.
The single defense for both is the same: the verifier decides the acceptable algorithm, not the token. Pin the expected algorithm (or a small allow-list) in your verification call and reject anything else.
Skipping the claims
A valid signature proves the token was issued and not altered. It does not prove the token is for you or still valid. A correct verifier also checks:
exp(expiry) andnbf(not-before): reject expired or not-yet-valid tokens, allowing a small clock-skew tolerance.aud(audience): confirm the token was meant for this service. A token issued for service A must not be accepted by service B.iss(issuer): confirm it came from the issuer you trust, and resolve the verification key from that issuer rather than from the token.
Omitting these is how a stolen or misdirected token gets accepted where it should not be.
The smaller traps
- Weak HMAC secrets. An HS256 token signed with a short or guessable secret can be brute-forced offline until a matching signature is found. Use a long, random key.
kidand JWKS handling. Thekidheader selects a key; treat it as untrusted input (it has been used for injection), and fetch keys only from your trusted issuer's JWKS.- Decoding mistaken for verifying. Reading a token's claims without checking the signature and timing is not verification. Act only on a fully verified token.
The short version
Pin the algorithm, validate exp, aud, and iss, use strong keys, and never confuse decoding with verifying. The JWT tool decodes a token, shows its claims and expiry in plain language, and verifies an HMAC signature against a secret you paste, all in your browser, with the token and secret never sent anywhere.