JWT Security Review
Review JWT-based authentication end to end.
Review Areas
Algorithm Handling
- Pin the expected algorithm server-side; reject
alg: noneoutright - RS256→HS256 confusion: if the server verifies HMAC using the public key as the secret, an attacker can forge tokens. Confirm verification uses the correct key type per algorithm
- Watch for
jku/x5uheader injection where the server fetches keys from an attacker-controllable URL kidinjection: path traversal or SQL/Command injection in key lookup
Key and Secret Strength
- HS256 secrets: ≥32 bytes of entropy, not from wordlists (
jwt-toolscrack mode) - Keys rotated; old keys revoked rather than just unused
Claims Validation
expandnbfenforced, clock skew boundedissandaudvalidated against expected values- Reject tokens with unexpected/extra privileged claims (e.g.,
admin: true) — parse then validate allowlist - Don't trust client-controlled
role/uidclaims if a server-side lookup exists
Token Lifecycle
- Refresh tokens: rotation with reuse detection; stored hashed server-side
- Revocation path exists for logout/password change/admin force-logout
- Access tokens short-lived (≤15 min); no long-lived bearer tokens
- Tokens not persisted in localStorage if XSS surface exists; prefer httpOnly cookies with CSRF protection
subis stable and unambiguous (user ID, not email that can change)
Common Code Smells
jwt.decode(token) // decode ≠ verify: signature never checked
jwt.verify(token, key) // no algorithms option pinned
jwt.verify(token, key, { algorithms: ['*'] })
Test Cases
- Remove the signature; send
alg: nonetoken - Flip
algto HS256 signed with the public key - Expired/missing
exp, wrongiss/aud - Tampered claims with valid signature (e.g., escalate role)
- Replay a rotated refresh token — should invalidate the family
Output
Findings with token samples (redacted), affected code paths, and remediation per issue.