1---2name: security-checklist3description: OWASP-style quick reference — authn/authz, injection, secrets, sessions, crypto, dependencies. Use during code review and before committing security-sensitive changes.4---56# Security checklist78## Authn / authz9- [ ] Authentication check happens on a path the caller can't influence (verified JWT, session cookie with `HttpOnly`/`Secure`/`SameSite`).10- [ ] Authorization is explicit in the handler, not implied by routing.11- [ ] Role/permission decisions use the server's identity model, never a client-supplied header.12- [ ] Multi-tenant check: the resource belongs to the caller's tenant (not just the caller's user).13- [ ] No IDOR: object IDs in URLs are checked against ownership, not just existence.1415## Input validation16- [ ] Validated at the edge (handler/controller), not deep in services.17- [ ] Schema-based where possible (zod/pydantic/struct tags).18- [ ] Every sink is aware of the shape feeding it (SQL parameters, shell args, template context, file paths, HTTP URLs, deserializers).1920## Injection surfaces21- [ ] SQL: parameters only. Never string-concat into a query.22- [ ] Shell: use argv arrays, never `exec(string)` with user input. Use a safe runner (`execFile`, `subprocess.run([...])`).23- [ ] Path: reject `..`, absolute paths, and symlinks that escape the allowed root.24- [ ] Template: auto-escape on; avoid `raw`/`|safe`; parameterize over string assembly.25- [ ] Deserializers: never `pickle`/`yaml.load`/`eval` on untrusted input.26- [ ] HTML rendering: sanitize + use a framework that auto-escapes; beware `dangerouslySetInnerHTML`.2728## Secrets29- [ ] Not in source.30- [ ] Not logged. Scrub before logging (redact keys like `password`, `token`, `secret`, `auth`).31- [ ] Read from env or a secrets manager.32- [ ] Rotated when in doubt.3334## Sessions / cookies35- [ ] `HttpOnly`, `Secure`, `SameSite=Lax` (or `Strict` when tighter is fine).36- [ ] Session rotates on privilege change (login, logout, role change).37- [ ] Logout invalidates server-side, not just client-side.3839## Crypto40- [ ] Use the platform library. Never roll your own.41- [ ] MD5 / SHA-1 are NOT for security. Use SHA-256+, bcrypt/argon2 for passwords.42- [ ] Encryption in authenticated mode (AES-GCM), never ECB.43- [ ] RNG: cryptographic (`crypto.randomBytes` / `secrets.token_*`), never `Math.random`.44- [ ] Secrets compared in constant time where applicable.4546## Transport47- [ ] TLS everywhere. Redirect HTTP → HTTPS.48- [ ] HSTS set for production domains.49- [ ] No secrets in query strings (URLs end up in logs).50- [ ] CSRF: token on state-changing endpoints (unless using strict CORS + SameSite cookies and you can prove it).5152## CORS53- [ ] Allow-list, not wildcard, for credentialed requests.54- [ ] `Access-Control-Allow-Origin: *` never with `Access-Control-Allow-Credentials: true`.5556## Output / response57- [ ] Return only fields the caller is entitled to — no "send the whole ORM object" leaks.58- [ ] Error messages don't expose internals (stack traces, SQL, file paths) to clients.5960## Dependencies61- [ ] `npm audit` / `pip-audit` / `bundler-audit` / `cargo audit` / `govulncheck` clean (or triaged).62- [ ] New dep has a real reason. Not "saw it on a blog post."6364## Rate limiting / abuse65- [ ] Expensive endpoints throttled per user/IP/tenant.66- [ ] Login is bruteforce-protected (rate limit + lockout threshold).67- [ ] Enumeration-resistant responses on auth endpoints.6869## File uploads70- [ ] Type detected from content, not extension.71- [ ] Size limits enforced before reading into memory.72- [ ] Served from a domain without session cookies (or via signed URL).7374## Logging75- [ ] No PII / secrets in logs.76- [ ] Log authn events (success, failure, logout).77- [ ] Include requestId for correlation.