# Security checklist

Work through this before launch and again after any significant change.

## Configuration
- [ ] `public_html/install/` deleted.
- [ ] `config.php` is `640` and outside the document root.
- [ ] `app.debug` is `false` and `app.env` is `production`.
- [ ] `app.force_https` is `true` and AutoSSL is active.
- [ ] `security.trust_proxy` is `true` **only** if you are genuinely behind a
      proxy you control. Leaving it on otherwise lets a client spoof its IP and
      defeat every rate limit.

## Verify by hand
- [ ] `https://domain/storage/` → 403.
- [ ] `https://domain/storage/documents/` → 403.
- [ ] `https://domain/config.php` → does not render or download.
- [ ] `https://domain/app/routes.php` → 403 or 404.
- [ ] `https://domain/assets/anything.php` → does not execute.
- [ ] `https://domain/admin` while signed out → redirects to sign-in.
- [ ] Response headers include `Strict-Transport-Security`,
      `X-Content-Type-Options`, `X-Frame-Options` and a CSP.

## Access control
- [ ] Each staff account has the narrowest role that lets them work. Support
      accounts must not hold `providers.manage` or `settings.manage`.
- [ ] Try opening another customer's order id while signed in as a test user.
      It must 404, not render.
- [ ] Try opening a case as an advocate it is not assigned to. It must 404.

## Payments
- [ ] Webhook secret is set and the Razorpay dashboard points at
      `https://domain/webhooks/payment`.
- [ ] Send a deliberately wrong signature to the webhook URL; expect 400.
- [ ] Confirm the webhook route is exempt from CSRF but not from signature
      verification.

## Data
- [ ] `config.php` backed up offline.
- [ ] Confirm the audit log contains no passwords, OTPs or API secrets.
- [ ] Confirm the CSV export contains no identity-document references.

## Ongoing
- [ ] Review `audit_logs` weekly for unexpected privileged actions.
- [ ] Review failed `login_logs` for credential-stuffing patterns.
- [ ] Rotate provider credentials if a staff member with access leaves.
