JWT SECURITY CHECKER

Paste a URL. The scanner collects the tokens your app issues, decodes the claims, and replays tampered variants to see whether your server actually verifies the signature — or just reads it.

TEST YOUR JWT HANDLING NOW

Enter your URL — we capture the tokens your app issues, decode the claims, and replay alg=none and re-signed variants to check whether verification is real.

What is JWT security testing?

A JSON Web Token is three base64 segments: a header naming the signing algorithm, a payload of claims, and a signature. Your server issues one after login and trusts its claims on every subsequent request. The signature is the entire security model — if it can be forged, skipped, or ignored, then “I am user 1 with role admin” is just a string an attacker types.

Testing JWT handling means checking what the server actually does with a token, not what the code says it does. Does it verify at all? Does it pin the algorithm, or read it from attacker-controlled input? Is the secret guessable? Does the token ever stop being valid? And is it stored somewhere a stray script can read?

This scanner collects the tokens your deployed app issues and answers those questions from the outside.

Why this happens in AI-generated apps

AI coding tools — Lovable, Bolt, v0, Cursor, Replit — produce auth code that works on the first try, which is exactly the problem. A login flow that returns a token and a frontend that stores it looks complete in a preview, and nothing in the preview distinguishes verified from decoded.

The recurring shapes are consistent. Verification helpers that read the algorithm from the token header, because the library’s permissive default was never overridden. Signing secrets that are placeholder strings from an example, kept because the app worked and nobody flagged them. Tokens issued with no exp field, because adding expiry means building a refresh flow and the model took the shorter path. Claims stuffed with the user’s email, name, and role, because it saved a database lookup in the component that needed them. And localStorage.setItem('token', ...), because that is the pattern in the most tutorials.

Each is a reasonable-looking line of code. Together they mean a token that never expires, holds personal data in the open, can be read by any script on the page, and may not be verified at all. See JWT alg=none and kid traversal for the exploitation detail, and OWASP Top 10 for AI-generated code for the surrounding failure modes.

What the scanner checks

ALG=NONE ACCEPTED

A token with the signature stripped and alg set to none is replayed. If it is honored, every claim is attacker-controlled.

ALGORITHM CONFUSION

An RS256 token re-signed as HS256 using the public key as the HMAC secret — the classic verification bypass.

WEAK HMAC SECRET

The captured token is tested offline against common, default, and short secrets. A hit means anyone can mint tokens.

MISSING EXPIRY

No exp claim, or an expiry so distant the token is effectively permanent and unrevokable.

EXPIRY NOT ENFORCED

exp is present but an expired token is still accepted — verification skipped the time check.

SENSITIVE CLAIMS

Email, phone, address, internal IDs, plan tier, or permission maps sitting in a payload anyone can decode.

LOCALSTORAGE STORAGE

Token written to localStorage or sessionStorage, reachable by any script including a compromised dependency.

WEAK COOKIE FLAGS

Token in a cookie without httpOnly, Secure, or SameSite — readable or sendable cross-site.

MISSING CLAIM CHECKS

No iss or aud validation, so a token minted for a different service or environment is accepted.

How it works

  1. Collect — we load your URL in a real headless browser and capture the tokens the app issues and sends.
  2. Decode — header and payload are parsed to read the algorithm, expiry, and every claim in the clear.
  3. Test the secret — for HMAC tokens, candidate secrets are checked offline against the captured signature.
  4. Replay tampered variantsalg=none, algorithm-confusion, and expired tokens are sent to see what the server accepts.
  5. Inspect storage — we check where the token lives: cookie flags, localStorage, or sessionStorage.
  6. Report — each finding shows the decoded token, what an attacker can forge, and the verification fix.

JWT configuration reference

Setting Safe configuration If wrong
Algorithm Pinned server-side in the verify call Attacker sets alg: none or swaps RS256 for HS256 and forges any claim
HMAC secret Long, random, from a secret store Cracked offline; attacker mints valid tokens for any user
Signing scheme Asymmetric (RS256 / ES256) for multi-service setups Every service that verifies also holds a key that can mint
exp Short-lived — minutes to hours, with refresh Token valid forever; password changes do not revoke it
Expiry enforcement Verified on every request Expired token still accepted; expiry is decoration
iss / aud Validated against expected values A token from staging, or another service, is accepted in production
Claim contents Opaque user id only Email, role, and internal IDs readable by anyone holding the token
Client storage httpOnly; Secure; SameSite cookie localStorage token exfiltrated by any XSS or compromised dependency
kid header Fixed key map, no path lookup Attacker points key resolution at a file or value they control
Key rotation Supported, with overlapping validity A leaked secret cannot be retired without logging everyone out at once

Common fixes

  • Pass the expected algorithm explicitly to your verification call and reject every other value. Never let the token’s own header decide how it is checked — that is the root of both alg=none and algorithm confusion.
  • Replace HMAC secrets with a long random value from a secret store, or move to asymmetric signing so services that only verify never hold a key that can mint.
  • Set a short exp on every token and build a refresh flow. Without expiry there is no revocation, because a signed token stays valid regardless of what happens to the account.
  • Validate iss and aud so a token minted for staging, or for a different service sharing the key, is not accepted in production.
  • Keep claims opaque. Carry a user id and look up everything else server-side; the payload is signed, not encrypted, and is readable by anyone who holds the token.
  • Move the token into an httpOnly; Secure; SameSite=Lax cookie so JavaScript cannot read it. If it must live in localStorage, keep expiry short and enforce a strict Content-Security-Policy.
  • Never trust claims for authorization without a server-side check. A role: admin claim is a hint about who the user says they are, not permission to act.
  • Rotate the signing key after fixing anything. Tokens issued under the broken configuration remain valid until the key that signed them is retired.

COMMON QUESTIONS

01
What is a JWT and why does its configuration matter?
A JSON Web Token is a signed, base64-encoded blob your server hands a client after login. The client sends it back on every request and the server trusts its claims. The signature is the only thing standing between a claim and a forgery — so every weakness in how that signature is produced or verified translates directly into account takeover.
Q&A
02
What is the alg=none vulnerability?
The JWT specification includes an algorithm value of 'none', meaning unsigned. If a server reads the algorithm from the token's own header and honors it, an attacker can strip the signature, set alg to none, edit any claim — user id, role, tenant — and be trusted. The fix is to pin the expected algorithm server-side and reject anything else, never to read it from the token.
Q&A
03
Why are weak HMAC secrets a problem?
HS256 signs with a shared secret. Because the whole token is available to whoever holds it, an attacker can test candidate secrets offline at high speed with no requests to your server. A short, dictionary, or default secret — 'secret', 'changeme', a framework placeholder — falls quickly, and once it does the attacker can mint valid tokens for any user.
Q&A
04
What happens if a token has no expiry?
It is valid forever. A token captured from a shared machine, a log file, a browser extension, or an old backup keeps working long after the password is changed, because password changes do not invalidate already-issued tokens. Short expiry plus refresh tokens is what makes revocation possible at all.
Q&A
05
Is it bad to put data in JWT claims?
Claims are signed, not encrypted. Anyone holding the token can base64-decode it and read every field — no key required. Email addresses, phone numbers, internal identifiers, plan details, and permission structures in claims are readable by the user, by anything that logs the token, and by any script that can reach it.
Q&A
06
Should I store JWTs in localStorage?
Prefer not to. Anything in localStorage is readable by any JavaScript running on the page, so a single XSS or a compromised dependency exfiltrates the token silently. An httpOnly, Secure, SameSite cookie keeps the token out of JavaScript's reach entirely. If localStorage is unavoidable, keep expiry short and pair it with a strict Content-Security-Policy.
Q&A
07
Is the scan safe to run on production?
Yes. It inspects tokens your app issues to an ordinary session and sends a small number of read-only requests with modified tokens to check whether verification is enforced. Nothing is written, no user data is modified, and captured tokens are not retained.
Q&A
08
How do I fix a JWT finding?
Pin the algorithm in the verification call rather than trusting the token header. Replace weak HMAC secrets with a long random value, or move to asymmetric signing so the verifier never holds a minting key. Set a short exp on every token and add a refresh flow. Move personal data out of claims and behind a lookup. Then rotate the signing key — every token issued under the old configuration must stop being valid.
Q&A

SCAN THE FULL AUTH FLOW

A valid token is only half of auth. The VibeEval agent tests the authorization behind it — RLS, object access, and API behavior.

RUN FULL SCAN