Security Audit Checklists
Use these checklists during audits. Mark each item: ✅ pass, ❌ fail, ⚠️ partial, N/A.
General Application
- All user input validated and sanitized at trust boundaries
- Output encoding applied contextually (HTML, URL, JS, CSS)
- Authentication required for protected resources
- Authorization checked on every request (not just UI hiding)
- Session tokens are cryptographically random, HttpOnly, Secure, SameSite
- Passwords hashed with bcrypt/argon2/scrypt (cost factor ≥ 10)
- Rate limiting on auth, password reset, and sensitive endpoints
- CSRF protection on state-changing requests (cookie-based auth)
- Security headers configured (CSP, HSTS, X-Frame-Options, etc.)
- Secrets not in source control or client bundles
- Error messages don't leak stack traces or internal paths in production
- Logging excludes PII, tokens, passwords
- File uploads validated (type, size, content, storage path)
API Security
- Input schema validation (Zod, Joi, class-validator, etc.)
- Authentication on all non-public endpoints
- Object-level authorization (no IDOR via predictable IDs)
- Pagination limits enforced (no unbounded queries)
- Mass assignment prevented (allowlist fields)
- GraphQL: depth/complexity limits, introspection disabled in prod
- REST: proper HTTP methods, no sensitive ops via GET
- API versioning and deprecation policy
- CORS allowlist (not
*with credentials) - Request size limits configured
Frontend / Client
- No secrets in client-side code or
NEXT_PUBLIC_*/VITE_*env vars -
dangerouslySetInnerHTMLavoided or sanitized (DOMPurify) - Third-party scripts loaded with SRI hashes
- postMessage handlers validate
event.origin - localStorage/sessionStorage doesn't hold sensitive tokens
- JWT not stored in localStorage (prefer HttpOnly cookies)
- Client-side routing doesn't bypass server auth
- Source maps disabled or restricted in production
Authentication & Identity
- MFA available for privileged accounts
- Account lockout / rate limiting after failed attempts
- Secure password reset flow (time-limited tokens, no user enumeration)
- OAuth state parameter validated (CSRF)
- OIDC nonce validated
- JWT: algorithm allowlist (reject
none, verifyalgheader) - JWT expiry enforced, refresh token rotation
- Logout invalidates server-side session
Data Protection
- Sensitive data encrypted at rest
- TLS 1.2+ enforced, HSTS enabled
- PII minimized and classified
- Database queries parameterized (no string concatenation)
- ORM raw queries audited for injection
- Backup encryption and access controls
- Data retention and deletion policies implemented
Infrastructure & DevOps
-
.envfiles gitignored, secrets in vault/CI secrets - Docker images scanned, non-root user, minimal base image
- CI/CD secrets scoped minimally, no secrets in logs
- Dependency lock files committed and verified
- Production debug mode disabled
- Admin interfaces not publicly exposed
- WAF / rate limiting at edge (if applicable)
Node.js Specific
-
NODE_ENV=productionin production -
child_processnot called with user input -
eval,new Function,vmavoided - Prototype pollution mitigated (
Object.create(null)for maps) -
express.json()body size limit set - Helmet or equivalent security middleware
- Path traversal prevented in file operations (
path.resolve+ prefix check)
Quick Scan Commands
# Dangerous patterns
rg -n "eval\(|new Function\(|innerHTML\s*=|dangerouslySetInnerHTML" --glob "*.{js,ts,jsx,tsx}"
# Hardcoded secrets (review each hit)
rg -n "(api[_-]?key|secret|password|token)\s*[:=]\s*['\"][^'\"]{8,}" --glob "*.{js,ts,json}" -i
# SQL injection risk
rg -n "query\(|execute\(|raw\(|\.query\(\`" --glob "*.{js,ts}"
# Missing auth middleware
rg -n "router\.(get|post|put|delete|patch)" --glob "*.{js,ts}" -A2
Checklist Output Format
When reporting checklist results:
## Checklist: [Category]
- ✅ Input validation at boundaries — Zod schemas on all API routes
- ❌ CSRF protection — cookie auth without CSRF tokens on POST /api/transfer
- ⚠️ Rate limiting — only on /login, missing on /api/*
- N/A MFA — internal tool, SSO handled by IdP