@@ -7,20 +7,54 @@ alwaysApply: false
77**AVOID JWT if you can.** Prefer opaque tokens with server-side sessions.
88
99Patterns {
10- (token in localStorage or sessionStorage) => Critical: XSS vulnerable. Use httpOnly Secure SameSite cookies.
11- (JWT 'none' algorithm) => Critical: Signature bypass. Use RS256/ES256.
10+ ## Session State
11+ any(refresh token rotation, refresh reuse detection, jti denylist, jti revocation,
12+ token bound to session, token bound to device, logout invalidates token server-side)
13+ => Critical: Prefer opaque tokens with server-side sessions. You're tracking state anyway.
14+
15+ ## Storage & Transport
16+ (token in localStorage or sessionStorage) => Critical: XSS vulnerable. Use httpOnly Secure SameSite=Strict cookies.
17+ (token in URL or query params) => Critical: Leaks via logs, Referer, browser history, analytics.
18+ (token logged or sent to analytics) => Critical: Scrub tokens from all logging pipelines.
19+ (SameSite=None or missing CSRF protection) => Critical: CSRF exposure. Use SameSite=Strict or add CSRF tokens.
20+
21+ ## Algorithm & Signature
22+ (JWT 'none' algorithm) => Critical: Signature bypass. Reject unsigned tokens.
1223 (JWT verification disabled) => Critical: Always verify signatures.
1324 (jwt.decode without verify) => Critical: Use jwt.verify().
14- (JWT expiration ignored) => Critical: Always check expiration.
15- (HS256 shared across services) => Critical: Use asymmetric algorithms.
16- (access token lifetime >= 1 day) => Warn: Use short-lived tokens + refresh rotation.
17- (JWT detected) => Warn: Consider opaque tokens + server sessions.
18- }
25+ (alg from token used to select verification) => Critical: Alg confusion. Strict allowlist + key type must match algorithm.
26+ (HS256 or symmetric algorithm) => Critical: Use asymmetric algorithms (RS256/ES256).
27+
28+ ## Verification Failure Handling
29+ (verification failure allows anonymous access) => Critical: Fail closed. Invalid token = no access.
30+ (verification failure allows partial or degraded access) => Critical: Fail closed. Invalid token = no access.
31+
32+ ## Token Purpose
33+ (ID token used as access token) => Critical: Wrong token type. Use access tokens (typ: at+jwt) for API auth.
34+ (typ header not validated) => Critical: Reject tokens without expected typ. Prevents token confusion.
35+
36+ ## Key Handling
37+ (kid used to fetch key from untrusted source) => Critical: SSRF/key injection. Allowlist kid values.
38+ (JWKS URL derived from iss without strict allowlist) => Critical: Attacker-controlled keys. Pin JWKS URLs.
39+ (JWKS endpoint not pinned or cached) => Warn: Cache JWKS with TTL. Validate kid against known set.
40+ (multi-issuer: verification keys shared across issuers) => Critical: Issuer key isolation required. One keyset per issuer.
41+
42+ ## Claims Validation
43+ (iss not validated) => Critical: Confused deputy. Verify issuer matches expected value.
44+ (aud not validated) => Critical: Token reuse across services. Verify audience includes this service.
45+ (exp not validated) => Critical: Always check exp claim.
46+ (nbf present and not validated) => Critical: If present, must validate. Reject on failure.
47+ (iat present and not validated) => Critical: If present, must validate. Reject on failure.
48+ (nbf and iat not checked when absent) => Warn: Consider requiring nbf/iat. Validate with ≤60s clock skew.
49+
50+ ## Authorization
51+ (roles or scopes trusted without server check) => Critical: Claims are assertions, not policy. Server must enforce.
52+
53+ ## Cookie Hardening
54+ (cookie missing __Host- prefix) => Warn: Use __Host- to enforce Secure, Path=/, no Domain.
55+ (cookie Domain set to parent domain) => Warn: Subdomain hijacking. Omit Domain or use __Host-.
1956
20- If JWT required {
21- - Asymmetric algorithms (RS256/ES256)
22- - Short expiration (≤15 min for access tokens)
23- - httpOnly Secure SameSite cookies
24- - CSRF protection
25- - Refresh token rotation
57+ ## Lifetime
58+ (access token lifetime >= 1 day) => Critical: Max 15 min for stateless JWT.
59+ (access token lifetime > 15 min and < 1 day) => Warn: Shorter is better. 15 min max recommended.
2660}
0 commit comments