
Loading

Loading
We use strictly necessary cookies to run this site, and analytics cookies to understand how it's used. See our Privacy Policy for details.
The handful of JWT implementation mistakes that turn a convenient auth mechanism into an authentication bypass, and how to check your own tokens for them.
JSON Web Tokens are a genuinely convenient way to handle stateless authentication, but "convenient" and "implemented correctly" aren't the same thing, and a handful of well-known JWT mistakes keep showing up in real applications. If you want to inspect a token's own header and payload while reading this, our JWT Decoder does exactly that, entirely in your browser.
alg: none bypassHistorically the most notorious JWT vulnerability. The JWT specification allows an alg value of none, meaning "this token has no signature." A poorly implemented verification library would happily accept a token with alg: none and skip signature checking entirely, meaning anyone could hand-craft a token claiming to be any user, with no valid signature required at all. Modern libraries reject this by default, but it's still worth explicitly confirming your stack does, rather than assuming.
A subtler version of the same idea. If a server verifies tokens with a public key for RS256 (asymmetric) tokens, but the verification code doesn't pin the expected algorithm, an attacker can craft a token with alg: HS256 and sign it using the server's own public key as an HMAC secret, since the public key is, by definition, publicly known. A verifier that trusts whatever alg the token claims, rather than enforcing the algorithm it expects, is vulnerable to this regardless of how strong the actual keys are.
For HS256/384/512 tokens, the entire security of the signature rests on the secret's strength. A short, guessable, or default secret ("secret", "changeme", a value copied from a tutorial and never rotated) can be brute-forced offline once an attacker has even one valid token to test guesses against, the token itself doesn't need to be intercepted in a live attack, just observed once. Use a genuinely random, sufficiently long secret, and treat it with the same handling as any other credential.
A JWT without an exp claim, or with one the server-side code never actually validates, is effectively a permanent credential once issued. If a token leaks (logged accidentally, cached, exposed in a URL), there's no time-based limit on how long it remains valid. Short-lived access tokens paired with a separate refresh-token flow is the standard mitigation, not because it's fashionable, but because it bounds the blast radius of a leaked token.
The payload of a JWT is base64url-encoded, not encrypted, anyone holding the token can decode and read it, trivially, with no key required. Tokens that embed sensitive personal data, internal roles, or anything not meant to be visible to the token's holder are a data-exposure risk independent of the signature being valid. If a claim needs to be confidential, a JWT (JWS) is the wrong tool, JWE (encrypted JWTs) or simply not embedding the data at all are the right ones.
Because JWTs are stateless by design, there's no built-in way to invalidate one before it expires, unlike a server-side session, which can just be deleted. For anything where immediate revocation matters (a compromised account, an offboarded employee), this needs an explicit mitigation: a server-side denylist, short expirations with frequent refresh, or a version/generation claim that gets bumped to invalidate all previously issued tokens at once.
A quick, practical exercise: paste a token your own application issues into our JWT Decoder and check, does it have a reasonable exp? Does the payload contain anything you wouldn't want visible to whoever holds it? If it's HS256, is the secret something you'd be comfortable being brute-forced against? None of this requires special tooling to check, just a few minutes and the right questions. For a deeper review of how authentication is actually implemented end-to-end, see our Web & API Security and Secure Code Review services. A hardcoded HS256 secret is a specific case of a broader pattern worth checking for across your whole codebase, see our piece on hardcoded secrets in code.
Tell us about your environment and goals, we'll help you scope the right engagement.