Repository navigation
feat: add Origin/Referer CSRF protection to state-mutating endpoints - #398
Conversation
Add a non-breaking, defence-in-depth CSRF check for state-mutating requests (POST/PUT/PATCH/DELETE). An onRequest hook compares the request Origin header (falling back to the Referer origin) against a configurable allowlist (CSRF_TRUSTED_ORIGINS) and rejects with 403 only when the header is present and its origin is not trusted. Requests with no Origin/Referer header (native/server-to-server clients) are always allowed, and when CSRF_TRUSTED_ORIGINS is unset the check is a complete no-op, so existing deployments and clients are unaffected. WHIP/WHEP media routes are exempt, mirroring their permissive CORS policy. Deliberately avoids cookies/sessions and @fastify/csrf-protection so it does not conflict with the separately-decided auth model. Closes #224 Co-Authored-By: Claude Opus 4.8 <[email protected]>
|
code-reviewer verdict: LGTM (automated self-review by a separate code-reviewer invocation; recorded as a marker because GitHub blocks state-bearing self-review when author and reviewer are the same account). A separate code-reviewer invocation independently re-ran the suite in a fresh clone of this PR: typecheck clean, lint 0 errors (317 pre-existing no-explicit-any warnings, unchanged), 439 tests pass across 22 suites. Verified the hook is non-breaking (allows requests with no Origin/Referer, no-op when CSRF_TRUSTED_ORIGINS is unset, never blocks GET/HEAD/OPTIONS, only 403s a present-but-disallowed Origin on POST/PUT/PATCH/DELETE), does not touch auth/cookies (no conflict with #397), and resists the usual bypasses (substring/suffix, Origin: null, Referer spoofing). Two non-blocking over-blocking nits (case-sensitive compare, default-port normalization) both fail closed on an opt-in feature. |
Summary
onRequesthook (src/csrf.ts) that verifies theOriginheader (falling back to theRefererorigin) of state-mutating requests (POST/PUT/PATCH/DELETE) against a configurable allowlist.CSRF_TRUSTED_ORIGINSenv var (comma-separated), resolved insrc/config/csrf-origin.ts.CSRF_TRUSTED_ORIGINSis unset/empty, and requests that carry noOrigin/Refererheader (native/server-to-server clients, tests) are always allowed. A 403 is returned only when a request presents an origin that is not in the allowlist.@fastify/csrf-protectionso it does not conflict with the separately-decided auth model (feat: add API key authentication to management endpoints #397). Documented the new env var inreadme.mdand.env.example.Test plan
CSRF_TRUSTED_ORIGINSand confirm a cross-origin POST from a disallowed browser origin is rejected with 403 while same-origin and non-browser clients succeed.Closes #224
🤖 Generated with Claude Code