Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

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_use claim 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_id filter 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:action string
  • User-scoped endpoints verify ownership (e.g., user_id matches sub claim)
  • 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(), not Math.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 dangerouslySetInnerHTML without 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