Skip to content
Token security

Security audit for JWT tokens

Paste a JWT token and get a security-first audit: detection of alg=none, brute force of an HS256 secret against a dictionary of 50 common secrets, missing claims (exp, iat, aud, iss, jti), suspicious lifetime above 30 days, kid headers with injection-prone characters, unpinned jku and x5u. For pure token reading (claim explainer, expiry countdown, optional HS256 verification) the JWT decoder is the dedicated tool; this audit assumes a valid signature and focuses on the security posture of the configuration.

How to use the audit

  1. 1

    Get a JWT

    Typical flow: capture the token from a browser HTTP request (DevTools > Network > Authorization: Bearer... header), or from application logs, or directly from the output of your identity provider during testing.

  2. 2

    Paste the token

    Three parts separated by dots: header.payload.signature. The tool validates the base64url format and fails with a clear message if any of the three segments is malformed.

  3. 3

    Read the findings

    Output: decoded header and payload (full text for manual inspection) + security findings list ordered by severity + indicative 0-100 score. Each finding has a normative reference and suggested remediation.

  4. 4

    Print or copy the report

    To integrate the report in an audit document or internal ticket: 'Copy report' produces a textual version; browser 'Print' for PDF export (print-optimized CSS hides nav and CTA).

Attack vectors and missing claims

The real risk in JWTs comes from configuration, not format. Reading a token's payload (what the JWT decoder does) is the easy part; asking whether that token is exposed to one of the well-known compromise patterns is what separates a production-grade integration from an incident waiting to happen.

Classic attack vectors checked by the audit. (1) alg=none: historically vulnerable JWT libraries (jsonwebtoken < 8.5, jose < 4 among the most notable) accepted {"alg":"none"} headers, bypassing signature verification. If the backend doesn't explicitly reject this value, an attacker can forge tokens with arbitrary payloads. (2) HS256 with weak secret: HMAC with a shared secret is brute-forcible offline if the secret is a dictionary word; the audit tries a dictionary of 50 common secrets and raises HIGH if one verifies the signature. (3) kid injection: the kid header is used by the backend as key path; if not sanitized it lends itself to path traversal (e.g. ../../../dev/null points to an empty file, the secret becomes the empty string, forgery succeeds) or SQL injection in the keys table. (4) jku/x5u trust: headers that declare the URL of the public key for self-verification; without backend pinning, an attacker controlling that URL controls the key.

Missing claims. RFC 7519 defines optional claims with security value: exp (expiration), nbf (not before), iat (issued at), jti (anti-replay), aud (audience, against confused deputy), iss (issuer, against rogue IdP). A frequent anti-pattern: tokens without exp live forever with no server-side revocation or automatic rotation; without aud, service A can forward a token to service B which accepts it because covered by the same IdP.

Operational limit. The audit does not verify the token signature: that requires the backend key (HS256 secret or RS256 public key). For optional HS256 verification there's the dedicated field of the JWT decoder; for RS256/ES256 verification requires fetching the IdP JWKS, which is the typical scope of the official provider SDK in debug mode. The audit assumes the signature is valid and focuses on configuration risks.

Audit rules applied

CheckSeverityReference
alg=none / alg=NoneCriticalMultiple CVEs (Auth0 lib 2015, jsonwebtoken < 8.5)
HS256 with secret in 50 common secrets dictionaryHighOWASP JWT Cheatsheet
exp missingHighRFC 7519 sec. 4.1.4
exp already expired (info)InfoRFC 7519
iat missingLowRFC 7519 sec. 4.1.6
aud missingMediumOWASP confused deputy
iss missingMediumOWASP rogue IdP
jti missingLowRFC 7519 sec. 4.1.7 (anti-replay)
Lifetime > 30 days (exp - iat)HighOWASP JWT Cheatsheet
kid present with suspicious chars (path traversal, SQLi)MediumOWASP injection
jku presentHighOWASP JWT spec abuse
x5u presentHighOWASP JWT spec abuse
typ missingInfoRFC 7519 sec. 5.1

Glossary

Technical terms used on this page, briefly explained.

JWT #
JSON Web Token, RFC 7519. String of format header.payload.signature, each base64url-encoded. Contains JSON claims about identity or authorization. De facto standard for modern API authentication.
alg=none #
Value of the alg header indicating no signature. RFC 7518 sec. 3.6 defines it as an option, but OWASP recommends rejecting it at application level to avoid signature bypass.
HS256 #
HMAC-SHA256, symmetric signature with a shared secret between issuer and verifier. The secret must be random (>= 256 bits of entropy, not a human passphrase) to resist offline brute force.
RS256 #
RSA-SHA256, asymmetric signature. The backend has the private key to sign, clients (or other services) have only the public to verify. Preferred over HS256 for multi-service architectures.
ES256 #
ECDSA-SHA256 with curve P-256. Equivalent to RS256 but with shorter keys (256 bits vs 2048-4096) and faster signatures. Recommended for new systems.
kid #
Key ID, optional header that identifies which key to use for verification (useful in multi-key JWKS). Known vulnerabilities if not sanitized backend-side (path traversal, SQLi).
JWKS #
JSON Web Key Set. Public endpoint that publishes the public keys of an IdP. Convention URL: https://idp/.well-known/jwks.json.
exp / iat / nbf #
Temporal claims (RFC 7519). exp: Unix expiration timestamp. iat: issued at. nbf: not before. All in seconds (not milliseconds).

Frequently asked questions about JWT audit

Does the tool decode the signature too? Can I see it?
The third segment (signature) is shown as a base64url string, not decoded. HMAC, RSA and ECDSA signatures are raw bytes and reading them adds no useful information. To verify it you need the key: for HS256 use the JWT decoder with the secret in the dedicated field (local WebCrypto computation), for RS256/ES256 your identity provider SDK in debug mode.
What happens if HS256 is detected with a weak secret?
The tool tries 50 common secrets in JS ("secret", "password", "key", "my-secret-key", "changeme", etc). If one verifies the signature, raises a HIGH finding saying "the HS256 secret is one of the 50 most common". Mitigation pattern: rotate the secret immediately to a 32+ byte crypto-safe random string. Generate it with: openssl rand -base64 32.
Expired token: is it a blocking finding?
No, classified as INFO. An expired token is NOT a security vulnerability (in fact, expiration is a mitigation). The tool reports the status to avoid false positives in the report (an audit that says "missing exp" on a token that has exp but is expired would be a bug).
Is lifetime > 30 days really a problem?
Depends on the use case. For user session tokens: yes, 30 days is too much, refresh token rotation with short-lived access tokens (15-60 min) is the recommended practice. For service tokens (JWT-formatted API keys): 30+ days can be acceptable if rotated and backend-revocable. The tool flags HIGH as conservative default: evaluate context and ignore if the use case justifies it.
kid injection: how is it actually exploited?
Real examples. (1) kid: "../../../dev/null" -> backend looks up key in filesystem -> finds empty file -> verifies HMAC with secret = empty string. Forgery. (2) kid: "key1' UNION SELECT 'attacker'-- " -> backend SQL on keys table -> SQLi -> attacker-controlled key. Mitigation: treat kid as untrusted input, alphanumeric whitelist + exact lookup in internal map.
Why are jku/x5u declared high-risk?
They allow the token to declare the URL of the public key to verify itself. If the backend uses it as-is (without pinning to trusted domains), an attacker controlling the URL controls the key -> valid signatures at will. Mitigation: pinning whitelist of authorized hostnames at backend level, or ignore jku/x5u and use only a hardcoded JWKS endpoint.
Does the tool decode JWE (encrypted) tokens?
No. JWE (RFC 7516) has 5 segments instead of 3 (header.encrypted_key.iv.ciphertext.tag) and the payload is encrypted, not readable without the key. JWT (RFC 7519) is typically signed-only (JWS), the payload is plain base64url. The tool handles classic JWS.
Can I audit a Refresh Token?
Yes, syntactically refresh tokens are often JWTs. Relevant audit points: (a) exp should be long (typical days-months), (b) jti present for anti-replay (refresh tokens should be invalidated on first use), (c) aud restricted to refresh endpoint, (d) ideally HS256 replaced by RS256 in multi-service arch.
Can I audit the entire flow, not just a single token?
This audit reports the risks visible from a single token. Examining the full flow (key rotation, refresh token rotation, server-side logout, anti-replay via jti, audience binding, production handling of kid and jku/x5u, OIDC provider configuration) requires an architectural review that crosses code, provider configuration and infrastructure: see the CTA at the bottom of the page.

Does your JWT architecture survive a serious audit?

This tool flags risks visible from a single token. A professional review covers the full flow: provider configuration, key rotation, refresh tokens, server-side logout, anti-replay (jti), audience binding, kid handling, jku/x5u. PHP, Laravel, Symfony, Node, Python backends.

JWT architecture review

Want a realistic estimate for your project?

7-question wizard, 2 minutes, free. Output: range of person-days, rough price range, engagement recommendation. Reference rate 300 EUR/day. Built for custom backend projects, integrations, security audits or AI automation.

Get a free quote