# Security Middleware Operations

## Purpose

Phase 6 places an ordered, fail-closed security boundary around every current and future HTTP route. The controls reduce common application-layer risk, but they do not replace TLS termination, a reverse proxy, Cloudflare/WAF controls, database permissions, monitoring, backups, or incident response.

## Middleware order

| Order | Control                   | Security purpose                                                     |
| ----: | ------------------------- | -------------------------------------------------------------------- |
|     1 | Request ID                | Correlates every response, log, and security event                   |
|     2 | Redacted request logging  | Records metadata without query strings, bodies, cookies, or secrets  |
|     3 | Helmet and CSP            | Restricts executable/content sources and hardens browser behavior    |
|     4 | API cache policy          | Prevents intermediary/browser storage of API and health responses    |
|     5 | Exact-origin CORS         | Allows credentialed browser requests only from configured origins    |
|     6 | Global rate limit         | Applies an IP-derived, non-plaintext key before expensive processing |
|     7 | Content-type gate         | Rejects unsupported bodies before parsing                            |
|     8 | CSRF verification         | Protects unsafe cookie-authenticated requests                        |
|     9 | Bounded parsers           | Limits JSON/form size and form parameter count                       |
|    10 | Maintenance and ban gates | Uses server-side providers; never trusts client-supplied status      |
|    11 | Audit hook                | Records controller-selected mutation context after response finish   |
|    12 | Routes and central errors | Keeps success/failure envelopes stable and prevents error disclosure |

Route-level Zod validation and idempotency middleware are exported for the business routes that own their schemas and transaction semantics.

## Browser response policy

Helmet sets a local-asset Content Security Policy with `object-src 'none'`, `script-src 'self'`, `script-src-attr 'none'`, `base-uri 'self'`, and a deployment-controlled `frame-ancestors` list. Production also upgrades insecure subrequests and enables one-year HSTS.

Telegram must be able to host the Mini App. For that reason, legacy `X-Frame-Options` is disabled and the more precise CSP `frame-ancestors` directive is authoritative. Keep `CSP_FRAME_ANCESTORS` narrow and deployment-managed. Never turn it into a runtime Admin setting.

The Permissions Policy disables camera, microphone, geolocation, payment, USB, and motion sensors because the current product does not need them. Add a capability only with an explicit product requirement and a security review.

## CORS policy

- Browser origins must exactly match an entry in `CORS_ALLOWED_ORIGINS`.
- Credentialed responses never use wildcard origins.
- Requests without an `Origin` header remain possible for health monitors and trusted server-to-server clients.
- Approved preflights advertise a fixed method/header set and a ten-minute cache.
- Rejected origins receive `ORIGIN_DENIED` and are recorded without logging the untrusted origin value.

`CORS` is a browser access policy, not authentication and not CSRF protection.

## CSRF contract

Clients obtain a token with:

```text
GET /api/v1/security/csrf
```

The response sets a same-site CSRF cookie and returns the same signed token in the JSON body. Secure deployments use the host-only `__Host-` cookie prefix. For `POST`, `PUT`, `PATCH`, and `DELETE`, the client sends the token in `X-CSRF-Token`; the cookie and header must match, the HMAC must verify, and the timestamp must be within `CSRF_TOKEN_TTL_SECONDS`.

Tokens use HMAC-SHA-256 and timing-safe verification. The binding resolver uses a verified session identifier when available. The Telegram auth exchange and secret-verified Telegram webhook are explicitly exempt because they do not yet rely on an existing cookie session; their owning phases must validate Telegram signatures, freshness, webhook secret, and session rotation.

## Rate limiting and Redis

Development/tests may select the in-memory fixed-window store. Production validation requires `REDIS_ENABLED=true` and `RATE_LIMIT_STORE=redis`. The Redis store increments and assigns TTL in one Lua script so multiple web instances share an atomic counter.

| Variable                   | Default | Purpose                                |
| -------------------------- | ------: | -------------------------------------- |
| `RATE_LIMIT_WINDOW_MS`     |  60,000 | Fixed window duration                  |
| `RATE_LIMIT_GLOBAL_MAX`    |     120 | Requests per client key per window     |
| `RATE_LIMIT_AUTH_MAX`      |      10 | Future login/exchange route budget     |
| `RATE_LIMIT_SENSITIVE_MAX` |      20 | Future reward/withdraw mutation budget |

Route budgets cannot exceed the global budget. A Redis counter failure returns `503 REDIS_UNAVAILABLE`; it never silently disables throttling. Readiness also fails while required Redis is unhealthy.

Application rate limiting must be paired with edge rate limiting, connection limits, request timeouts, caching, queue back-pressure, and monitoring. It cannot absorb a volumetric DDoS attack by itself.

## Body, validation, and errors

- Body methods accept JSON, `application/*+json`, or URL-encoded forms only.
- `REQUEST_BODY_LIMIT` applies to JSON and form parsers; URL-encoded input is capped at 100 parameters.
- Oversized, malformed, and unsupported bodies return stable `413`, `400`, and `415` errors.
- Zod validation exposes issue paths/codes/messages but not rejected input values.
- Unknown exceptions return only `INTERNAL_ERROR`; logs continue to redact tokens and URL credentials.

File uploads are not enabled in this phase. Phase 15 must use a dedicated streaming limit plus MIME signature, pixel, extension, quarantine, and authorization checks instead of these JSON/form parsers.

## Identity-dependent gates

Maintenance, ban, audit, and idempotency components are dependency-injected foundations:

- Maintenance state comes from a trusted server provider and preserves liveness endpoints.
- Ban state is checked only after a later authentication phase attaches a verified user ID.
- Audit data is chosen by trusted controller code and written asynchronously after the response.
- Idempotency keys are validated and HMAC-digested before a future repository reserves them.

No client field may grant an Admin bypass, clear a ban, change a reward, or mark an idempotency reservation successful.

## Verification commands

```bash
npm run test:security
npm run test:unit
npm run test:integration
npm run verify:environment:example
npm run check
npm audit --omit=dev
```

No live Redis, MySQL, Telegram bot, or deployment environment is contacted by the automated suite. Integration boundaries use deterministic test doubles.

## Primary references

- [Express production security](https://expressjs.com/en/advanced/best-practice-security/)
- [OWASP CSRF Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html)
- [OWASP HTTP Headers Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html)
- [OWASP Denial of Service Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Denial_of_Service_Cheat_Sheet.html)
