Security Review Checklist
Use this checklist when reviewing pull requests that touch authentication, authorization, or security-sensitive code.
Authentication & Tokens
- JWTs are verified with
jose.jwtVerify()using JWKS, not manual decoding - Algorithm is restricted to RS256 (no
alg: "none"or HS256 accepted) -
token_useclaim is checked to prevent token confusion - Required claims (
sub,iat,exp) are validated - Token lifetimes are appropriate (access: 1 hour, session: 24 hours, refresh: 30 days)
Database & SQL
- All queries use parameterized statements (
?placeholders with.bind()) - No string interpolation of user input into SQL queries
- Dynamic column names are validated against an explicit allowlist
-
app_idfilter is present on all tenant-scoped queries - Sensitive data (tokens, secrets) is hashed/encrypted before storage
Input Validation
- All request bodies validated with Zod schemas
- URL parameters validated (format, length, allowed characters)
- Redirect URIs validated by
validateRedirectUri() - IdP URLs validated for SSRF (HTTPS required, private IPs blocked)
- File uploads validated for type and size (when applicable)
CSRF & CORS
- State-changing endpoints don't bypass CSRF checks
- New API routes are covered by the Origin/Referer middleware
- CORS origins are not set to wildcard in production
- SameSite cookie attributes are preserved on new cookies
Authorization
- Admin endpoints are protected by
requirePermission()middleware - Permission checks use the correct
resource:actionstring - User-scoped endpoints verify ownership (e.g.,
user_idmatchessubclaim) - No horizontal privilege escalation (user A can't access user B's data)
- Disabled user check is not bypassed
Cryptography
- Secrets are hashed with PBKDF2 (100k iterations minimum)
- Random values use
crypto.getRandomValues(), notMath.random() - Encryption uses AES-256-GCM with unique IVs
- Comparison of secrets uses constant-time operations
- No secrets or keys hardcoded in source code
Response Security
- Error messages don't leak internal details (file paths, stack traces, SQL)
- User enumeration is prevented (generic responses for email lookups)
- Security headers are not removed or weakened (HSTS, CSP, X-Frame-Options)
- Sensitive data is not included in logs (
console.log,console.error)
OAuth & OIDC
- PKCE is still required (S256 method, no plain)
- Authorization codes are single-use (atomic consumption)
- Redirect URIs are validated against registered client URIs
- State parameter is validated and consumed atomically
- Token endpoint authenticates the client
Session Management
- New session creation sets appropriate cookie attributes (HttpOnly, Secure, SameSite)
- Session revocation is handled correctly (revoked_at timestamp set)
- User disable triggers proactive session revocation
- Session queries filter by
revoked_at IS NULL
Frontend
- No
dangerouslySetInnerHTMLwithout sanitization - External content (error messages from IdPs) is escaped
- Sensitive data (tokens, secrets) is not stored in localStorage
- Form submissions are disabled during processing (prevent double-submit)
- Redirect targets are validated before
navigate()calls
Configuration
- No new secrets hardcoded (use
wrangler secret put) - Environment variables have sensible defaults for development
- Production-only checks are gated on ISSUER being HTTPS
- New bindings are documented in wrangler.jsonc