The real risk in JWTs comes from configuration, not from the format. Reading the payload of a token (what the JWT decoder does) is the easy part: asking whether that token is exposed to one of the known compromise patterns is what separates a production-grade integration from an incident waiting to happen. Historical CVEs on widely-used JWT libraries (jsonwebtoken < 8.5, jose < 4, python-jwt, ruby-jwt) all stem from implicit backend assumptions that this tool surfaces from the token's manifest alone.
The four classical vectors verified by the audit. (1) alg=none and permutations: historically vulnerable libraries accepted {"alg":"none"} bypassing signature verification. The check covers the exact string and all case-mix permutations (None, NONE, nULL, null, empty alg, numeric alg, missing alg). (2) HS256 with weak secret: HMAC with a shared secret is brute-forceable offline if the secret is a human passphrase. The audit tries a curated dataset of 10,000 known secrets (wallarm/jwt-secrets + SecLists/scraped-JWT-secrets, recompiled nightly), verifying each candidate via local WebCrypto HMAC-SHA256. (3) alg confusion attack: the backend is configured for RS256 but also accepts HS256, and uses the RSA public key as the HMAC secret. Documented vector in historical CVEs (jsonwebtoken pre-8.5). If you provide the public key, the tool tries HMAC verification using the pubkey as secret and reports critical if it passes: it means the backend would accept attacker-controlled tokens signed with its own public key (which is public by definition). (4) kid injection / jku-x5u abuse: headers that declare which key to use for verification. If the backend doesn't sanitize them (kid used as key path or SQL lookup) or doesn't pin them to trusted domains (jku/x5u), an attacker who controls the input controls the key.
Missing claims. RFC 7519 defines optional claims with concrete security value: exp (expiration, time bound), nbf (not before, activation delay), iat (issued at, emission timestamp), jti (JWT ID, anti-replay), aud (audience, mitigation against confused deputy), iss (issuer, mitigation against rogue IdP). Frequent anti-pattern: tokens without exp, living forever with no server-side revocation; or without aud, where service A can forward the token to service B that accepts it because they share the same identity provider, even if it wasn't intended for B.
Esoteric headers. Two lesser-known headers the tool flags as info: cty (content type, enables nested JWT - a JWT inside a JWT, a confusion vector if the backend unwraps without re-authenticating), zip (compression: the header {"zip":"DEF"} enables DEFLATE decompression of the payload, and has been a vector for zip-bomb attacks against libraries that didn't limit output size).
Operational privacy. A JWT token contains the user-claim payload and the signature configuration in the header: in real audits you don't want to upload it to a third-party service, even over TLS. Here no network request leaves with token data. The secrets dataset (10,000 entries, ~180 KB raw) is downloaded by the browser only on first use and cached in localStorage for subsequent sessions (automatic invalidation via SHA-256 over the public file). Audit, JWT parsing, HMAC verification, secret brute-force, pubkey reading: all local via the WebCrypto API.
When static triage is the right level. Typical scenarios. (a) Preliminary review of a legacy API without backend access: you captured a token from the frontend and want to know if the server-side configuration is solid. (b) Pre-engagement audit to estimate the cost of a possible JWT hardening contract before quoting. (c) Initial screen before a serious cyber assessment: the high/critical list deserves deeper exploitability assessment, mapping of verify codepaths in the backend, verification of the algorithm whitelist and JWKS pinning. (d) Periodic self-check by the API owner who wants to know whether the integration was done properly.
Scope limit. The audit does not replace: an architectural review of the full flow (key rotation, refresh token rotation, server-side logout with jti blacklist, audience binding, kid and jku/x5u handling in production, OIDC provider configuration); static analysis of the verify code in the backend (algorithm whitelist, exp leeway, exception handling); the application-specific threat model. It is a triage and presents itself as one.