Production Security Checklist
Overview
A 14-point audit for a web app or API before it goes to production — and again whenever a release touches authentication, data access, storage, or backups.
Every check states the evidence that passes it, so the agent (or the reviewer) can produce a verdict per item instead of an opinion. Checks 1–9 cover the pre-deploy surface; checks 10–14 cover account deletion and the data that survives it (backups, replicas, analytics, third parties).
When to Use
- Deploying a new web app / API to production, or promoting a release candidate.
- Reviewing any change to auth, authorization, storage, or backup handling.
- Answering "is this production-ready?" before a handover, demo, or audit.
- Designing account deletion, data retention, or GDPR / UU PDP compliance.
Don't use for: host/OS hardening, cluster policy, network segmentation, or full penetration-testing scope. This checklist is application-layer.
How to Run It
- Walk checks 1–9 against a running environment (staging is fine, production
data is not needed). For each check, record
pass,fail, orunknownplus the evidence you actually saw — never markpassfrom reading code alone when the check can be exercised. - Walk checks 10–14 against the data map: database, replicas, backups, object storage, logs, analytics, caches, and third-party processors.
- Report failures with the smallest reproducible proof (request + response, or the query plan) and the fix. Completion criterion: every one of the 14 checks has a status and evidence, or is explicitly marked out of scope.
Part 1 — Before Deploy
1. Per-user authorization (IDOR)
Ask: if I'm logged in as A, can I reach B's data?
Pass: every read/write is scoped to the authenticated identity
(WHERE owner_id = :current_user), resource IDs are unguessable (UUIDv4, not
sequential), and a request for someone else's object returns 404/403.
Fail: editing an ID in the URL or request body returns another user's record.
Fix: enforce ownership in the data layer, not in the template or the client.
2. Password-reset link expiry
Ask: do reset (and invite / OTP / magic) links expire? Pass: single-use, time-limited (15–30 minutes is typical), bound to the user, invalidated by use and by any password change. Fail: a link generated yesterday still resets the password today.
3. Sanitize and validate every field
Ask: is every field sanitized on the server?
Pass: parameterized queries or ORM bindings (no string-built SQL), output
encoding appropriate to context (HTML/JS/URL), and server-side validation on
every input, including query params, headers, webhook payloads, filenames, and
uploaded file metadata.
Fail: ' OR 1=1-- returns data, or a <script> payload is echoed unescaped.
Client-side validation alone is never a pass.
4. Don't expose the API to the whole world
Ask: is the API open to anyone?
Pass: requests are restricted to expected origins (CORS allowlist, no
* combined with credentials), internal/admin endpoints are restricted by
network or auth, and unused routes/methods are closed.
Fail: wildcard CORS with cookies, or an admin route reachable unauthenticated.
5. Rate limiting
Ask: are limits applied?
Pass: per-IP / per-user / per-key limits on authentication, write, and
expensive endpoints (search, export, report), returning 429 with Retry-After.
Fail: login, reset, or OTP can be hammered thousands of times unthrottled.
6. Safe error handling
Ask: what does a failure state look like to the client? Pass: a custom error page or JSON body per failure state; no stack traces, SQL, file paths, hostnames, or framework versions; detail goes to server logs only. Fail: a 500 that leaks the stack, or a 200 response carrying an error payload (monitoring will never fire).
7. Indexes on the main queries
Ask: do the hot queries use indexes?
Pass: filtered/joined columns on high-traffic queries are indexed, verified
with EXPLAIN (index scan, not sequential scan).
Fail: sequential scans on large tables — or the opposite extreme, every column
indexed, which pays write and storage overhead for nothing.
8. Logging and monitoring
Ask: how will you know it broke at 3 a.m.? Pass: structured logs for auth events, admin actions, and errors; alerts on critical failures (5xx rate, queue depth, repeated failed logins, cron silence). Fail: the first signal is a user complaint.
9. Rollback plan
Ask: if this deploy crashes, can you roll back? Pass: blue-green (or canary/rolling with the same property) — a bad release is undone by a switch, without re-running migrations, and the previous version is verified to still work against the current schema. The rollback is rehearsed and timed, not theoretical. Fail: "rollback" means restoring last night's backup.
Part 2 — Account Deletion and the Data Behind It
10. Deletion is more than one DELETE
Ask: when a user deletes their account, is their personal data really gone? Pass: erasure covers the production database and backups, replicas, object storage, search indexes, caches, analytics/warehouse, log retention, and third-party processors, within the retention window the policy promises. Fail: data is deleted from production but still sitting in the last 30 days of backups — that is a reportable GDPR / UU PDP gap, not a technicality.
11. Never erase by restoring, editing, and recompressing backups
Ask: how do you plan to remove one user from historical backups? Pass: you don't touch the backups at all (see check 12–13). Fail: a job that rehydrates, edits, and re-uploads terabytes per deletion request — slow, expensive, and a great way to corrupt your own backups.
12. Encrypt sensitive data per user, under a per-user key
Pass: envelope encryption — a per-user data key encrypts that user's sensitive fields; the key itself is wrapped by a key-encryption key held in a managed key store (KMS/HSM/vault). Keys are never stored next to the ciphertext. Fail: one global application key — destroying it would mean re-encrypting everyone, which is not erasure.
13. On deletion, destroy the key — not the data
Pass: account deletion destroys the user's data key. Backups are untouched; the ciphertext inside them becomes permanently undecryptable ("cryptographic erasure" / crypto-shredding). Fail: a deletion flow whose correctness depends on finding every copy of the data across every store — that path fails silently as the system grows.
14. Prove the erasure
Pass: the erasure path is documented, key destruction is audit-logged (who, when, which subject), and a restore test confirms the deleted user's data is unreadable after backup recovery. Fail: no evidence trail — an auditor asking "show me" gets a shrug.
Deep dive on checks 12–14, key hierarchy, rotation, and the restore test:
see references/cryptographic-erasure.md.
Common Pitfalls
- Treating "logged in" as "authorized". Authentication is not authorization.
- Sanitizing only the fields you remember — headers, webhook payloads, and file metadata are inputs too.
- Client-side validation counted as validation.
- Rate limiting only login, forgetting reset, OTP, signup, search, and export.
- Error responses that return 200 with an error body — alerts never trigger.
- Indexing every column instead of the ones the hot queries actually use.
- Treating backups as out of scope for deletion requests.
- One global encryption key, which makes "destroy the key" impossible without a full re-encryption.
- Verifying authorization from the UI only — test with two real accounts, both directions (A→B and B→A), including direct API calls.
- Sketching a rollback plan that has never been executed once.
Verification Checklist
- Cross-account access tested with two users; returns 403/404
- Reset / OTP / invite tokens are single-use and time-limited
- All database access parameterized; server-side validation on every field
- Origin allowlist in place; no wildcard CORS with credentials
- Rate limits active on auth, write, and expensive endpoints
- Error responses leak no internals; alerts wired to 5xx and cron silence
- Hot queries verified with
EXPLAIN(index scan, not sequential scan) - Rollback rehearsed and timed on a real environment
- Account deletion covers backups, replicas, analytics, logs, third parties
- Per-user keys live in a managed key store; destruction is audit-logged
- Restore test passed: recovered backup yields unreadable deleted-user data
Source
Distilled from two Instagram reels by Mohamad Al Sayed (software-engineering
security series) — one on pre-deploy hardening, one on account deletion and
backups. Rules restated and expanded with pass/fail evidence and verification
criteria. Original reels: instagram.com/reel/Dc0b0K-thYr,
instagram.com/reel/DdB4g89NEbJ.