JWT: structure and vulnerabilities
1) What is JWT and where is it used
JWT is a compact self-contained claims container in the 'Base64Url (header) format. Base64Url(payload). Base64Url(signature)`.
Used for:- JWS (signed tokens - authenticity/integrity),
- JWE (encrypted tokens - privacy),
- OIDC/OAuth2 as access/ID tokens as well as service-to-service authentication.
Pros: autonomy, cachability, low overhead. Cons: risk of incorrect validation, complex recall cases.
2) JWT structure
2. 1 Header (JSON)
Minimum: algorithm and key identifier.
json
{ "alg": "ES256", "kid": "jwt-2025-10", "typ": "JWT" }
'alg ': signature/encryption algorithm (RS256/ES256/PS256/HS256, etc.).
'kid ': pointer to key (for JWKS rotation).
Optional key sources: 'jku', 'x5u' (see vulnerabilities § 6. 3).
2. 2 Payload (JSON)
Standardized stamps:- `iss` (issuer), `aud` (audience), `sub` (subject)
- 'exp '(expiration time),' nbf '(not before),' iat '(issued)
- 'jti '(token ID, revocable)
- domain-stamps: 'scope/roles', 'tenant', 'kyc _ level', etc.
2. 3 Signature
JWS = `sign(base64url(header) + "." + base64url(payload), private_key)`
Verification: strictly corresponding public key and exactly the algorithm that the server expects.
3) Basic check invariants
1. The algorithm is fixed by the configuration of the resource server (allow-list), and is not trusted with the contents of the'header. alg`.
2. Check 'iss' and 'aud' for an exact match, 'exp/nbf' - taking into account the small 'clock _ skew' (± 30-60s).
3. Deny tokens without 'kid' only if there is a single key and no rotation; otherwise, demand 'kid'.
4. Do not trust any stamps without object-level authorization (BOLA-first).
5. Parsing - after crypto verification; basic size checks prior to decoding.
4) JWS vs JWE
JWS: signed but readable. Do not put in payload PII/secrets.
JWE: encrypts payload; integration is more difficult, the key model is critical.
In most APIs, JWS + prohibition on sensitive data in payload is enough.
5) Token life cycle
Access: short term (5-30 minutes).
Refresh: longer (7-30 days), rotate-on-use (one-time), keep the "black list" 'jti/sid'.
Revocation: lists' jti'with TTL, introspection for opaque tokens, abbreviation' exp'for incidents.
Key rotation: JWKS with overlap (old + new), see the article "Key rotation."
6) Frequent vulnerabilities and how to close them
6. 1'alg = none '/algorithm substitution
Bottom line: The server trusts the'alg 'field and accepts an unsigned token.
Protection: hard allow-list of algorithms on the server; reject'none 'and unexpected values.
6. 2 RS256→HS256 swap (symmetrization)
Bottom line: an attacker replaces' alg'with HS256 and uses the public key as an HMAC secret.
Protection: bind the key to the algorithm with configuration; do not mix symmetric/asymmetric providers in one'kid '.
6. 3 Key injection ('kid/jku/x5u')
Scenarios:- 'jku'points to an attacker-controlled JWKS (will slip its key).
- 'x5u '/' x5c'abuse of external certificates.
- 'kid'with/SQL path injection ('.. "/../privkey. pem"' or '"'OR 1 = 1 -- "').
- Ignore deleted'jku/x5u 'or filter by strict allow-list domains.
- 'kid'should only be used as a key in the local directory (table/cache), without file paths/SQL concatenations.
- JWKS load from trusted URLs, short TTL, signature/pinning channel.
6. 4 Weak secrets of HS256 (brute force)
Bottom line: HMAC secret is short/leaked → signature spoofing.
Protection: use asymmetry (RS/ES/PS) or secret length ≥ 256 bits, secrets - only in KMS.
6. 5 Missing/invalid stamps
No'aud '/' iss '/' exp '→ token is cross-serviceable or infinite.
Too long'exp '→ risk of compromise.
Protection: require full set of marks, 'exp' short, 'nbf '/' iat' validate with 'clock _ skew'.
6. 6 Replay and token theft
Essence: interception/repetition of a token (leak in logs, XSS, MitM without TLS).
Protection:- TLS везде, `Secure`+`HttpOnly` cookie, SameSite=Lax/Strict.
- DPoP/PoP (binding a token to a client key) and/or mTLS for partners.
- Short'exp ', refresh-rotation, device-binding.
6. 7 XSS/Storage Leaks
Bottom line: JWT storage in 'localStorage '/' sessionStorage' is → available to JS.
Protection: store access tokens in HttpOnly-cookie (if cookie model is possible) + strict CSP/Trusted Types.
For SPA without cookies - isolate the token in memory, live minimally, protect against XSS.
6. 8 CSRF
The bottom line: during cookie sessions, requests from a third-party site.
Protection: SameSite, anti-CSRF tokens (double submit), 'Origin/Referer' check, Fetch-Metadata filters.
6. 9 Oversize/size abuse
Gist: huge payload/headlines, DoS on parsing.
Protection: title/body size limits, early-reject 431/413, fixed set of stamps.
6. 10 Substitution 'typ '/' cty'
Essence: confusion of types ('typ: "JWT"'), nested JOSE objects.
Protection: ignore 'typ/cty' for security, rely on fixed algorithms and branding scheme.
7) Token storage and transfer
7. 1 Server APIs (machine-to-machine)
mTLS/HMAC, preferably asymmetry for JWT, channels through mesh.
Key store - KMS/HSM, scheduled rotation, overlapping JWKS.
7. 2 Browser clients
HttpOnly Secure Cookie для access/refresh; short TTL; update - via "silent refresh" with'SameSite = None'only under HTTPS.
Strict CSP, Trusted Types, protect against XSS; for SPA - if possible, do not store the token on disk.
8) JWKS, rotation and recall
JWKS is published by the authorizer; consumers cache for 5-15 minutes.
Rotation plan: add a new 'kid' → start them signing → after N days remove the old one from JWKS → purge.
Feedback: 'jti/sid' lists with TTL; in case of an incident, temporarily reduce 'exp' and force-logout (disable refresh).
9) Multi-tenant and data minimization
Include 'tenant '/' org' in the token only if necessary by architecture; otherwise, pull the attributes from the PDP by'sub '.
No PIIs; minimum set of stamps → less risk of leaks and correlation.
10) Practical validation checklist (resource server)
- Parsim only after verification of signature and basic size limits.
- 'alg' from configuration; reject unexpected ones.
- Check 'iss' ∧ 'aud' ∧ 'exp' ∧ 'nbf' ∧ 'iat' (with 'clock _ skew').
- Check'kid 'on local directory/JWKS (short TTL).
- Filter/normalize external pointers ('jku/x5u' - allow-list only).
- Limit stamp length/composition (scheme).
- Apply Object Resource Authorization (BOLA-first).
- Log 'kid', 'sub', 'aud', 'iss', 'jti', 'exp', 'tenant', 'trace _ id' (without PII).
- Metrics of signature errors, delays, rotation audits.
11) Examples of secure policies (pseudo)
11. 1 Configuration of algorithm expectations
yaml jwt:
expected_issuer: "https://auth. example. com"
expected_audience: ["wallet-service"]
allowed_algs: ["ES256"] # fix the jwks_url: "https ://auth. example. com/.well-known/jwks. json"
jwks_cache_ttl: 600s clock_skew: 60s required_claims: ["iss","aud","sub","exp","iat"]
11. 2 Dropping remote 'jku/x5u'
yaml reject_untrusted_key_sources: true allowed_jku_hosts: ["auth. example. com"] # if absolutely necessary
11. 3 Sample Feedback List (Redis)
pseudo if redis. exists("revoke:jti:" + jti) then deny()
if now() > exp then deny()
12) Observability and incidence
Метрики: `jwt_verify_fail_total{reason}`, `jwt_expired_total`, `jwks_refresh_total`, доля `kid`.
Logs (structured): 'iss/aud/sub/kid/jti/exp/tenant/trace _ id', reason for failure.
Dashboards: "expiring soon," a surge in validation errors, distribution by region/client.
Alerts: growth of'verify _ fail' (signature/algorithm), JWKS errors, share of expired tokens.
13) Antipatterns
Trust'alg 'from token; support'none '.
Long-lived access tokens and no refresh rotation.
HS256 with a short secret/secret in ENV without KMS.
Accept'jku/x5u 'from any domain; dynamically load JWKS without allow-list.
Put PII/secrets in payload JWS.
Store tokens in 'localStorage' when XSS risks exist.
Suppress validation errors (return 200 s "soft error").
No BOLA checks and relying only on 'scope/role'.
14) Specifics of iGaming/Finance
Brands: 'kyc _ level', 'risk _ tier', 'tenant', strict 'aud'.
Short TTL for write operations (deposits/outputs), PoP/DPoP or mTLS for critical routes.
Regulatory audit: unchangeable logs of inputs/failures, storage of logs within the boundaries of the region.
Partners/PSP have separate keys/' aud ', tenant keys and separate JWKS.
15) Prod Readiness Checklist
- Hard allow-list of algorithms; 'none' is not allowed.
- 'iss/aud/exp/nbf/iat/jti' are checked; short 'exp'.
- overlapped JWKS, short TTL cache; monitoring of 'kid' shares.
- Refresh — rotate-on-use; 'jti/sid'lists with TTL; review playbook.
- PoP/DPoP or mTLS on critical routes.
- HttpOnly-cookies for browser, CSP/Trusted Types vs. XSS; CSRF protection.
- No PII in payload; minimum set of marks.
- Validation and failure metrics/logs; alerts JWKS/verify_fail.
- Negative scenario tests: RS→HS swap, 'kid' -injections, 'jku' spoofing, oversize, clock-skew.
16) TL; DR
Fix the algorithm and keys on the server side, do not trust 'alg' from the token, demand 'iss/aud/exp/nbf/iat/jti'. Rotate keys through overlapping JWKS, keep tokens short-lived and refresh disposable. Store tokens securely (HttpOnly-cookie for the web), minimize stamps, do not put PII. Close 'jku/x5u/kid' vectors, avoid HS256 with weak secrets, add PoP/DPoP or mTLS on critical paths, and always do BOLA checks at the resource level.