Skip to content

fix(api): accept proxied same-origin requests in CSRF middleware#48

Merged
tobias-gp merged 1 commit intomainfrom
hotfix/requestorigin
Apr 29, 2026
Merged

fix(api): accept proxied same-origin requests in CSRF middleware#48
tobias-gp merged 1 commit intomainfrom
hotfix/requestorigin

Conversation

@tobias-gp
Copy link
Copy Markdown
Contributor

Summary

  • Behind a reverse proxy (nginx, Cloudflare, tunnel, etc.) the browser sends Origin set to the public URL, which won't be in corsOrigins unless the operator keeps APP_BASE_URL/CORS_ORIGINS in sync — every mutation then 403s with Forbidden: invalid request origin.
  • The CSRF middleware now also accepts an Origin that matches the public origin derived from the standard X-Forwarded-Proto + X-Forwarded-Host headers set by the upstream proxy. A genuine same-origin request is by definition not CSRF, and a foreign attacker still cannot forge a same-origin Origin header.
  • The bare Host header is intentionally not trusted; direct deployments are expected to configure APP_BASE_URL.

Test plan

  • `pnpm exec vitest run apps/api/src/middleware/csrf.test.ts` — 12/12 pass, including new cases:
    • allows POST when Origin matches the proxied X-Forwarded-Host
    • rejects POST when only Host (no X-Forwarded-Host) matches Origin
    • rejects foreign Origin even when X-Forwarded-Host matches the foreign origin
  • Smoke-test in the deployed environment: previously-failing UI mutations no longer return Forbidden: invalid request origin.

Made with Cursor

Behind a reverse proxy (nginx, Cloudflare, tunnel, etc.) the browser sends
Origin set to the public URL, which won't be in `corsOrigins` unless the
operator keeps `APP_BASE_URL`/`CORS_ORIGINS` in sync — every mutation then
fails with "Forbidden: invalid request origin".

Allow Origin to also match the public origin derived from the standard
`X-Forwarded-Proto` + `X-Forwarded-Host` headers set by the upstream
proxy. A genuine same-origin request is by definition not CSRF, and a
foreign attacker still cannot forge a same-origin Origin header. The bare
`Host` header is intentionally not trusted; direct deployments are
expected to configure `APP_BASE_URL`.

Made-with: Cursor
@railway-app
Copy link
Copy Markdown

railway-app Bot commented Apr 29, 2026

🚅 Deployed to the archmax-pr-48 environment in archmax SemLayer

Service Status Web Updated (UTC)
archmax_external_dbs 🕗 Deploying (View Logs) Apr 29, 2026 at 2:13 pm
archmax_standalone_with_volume ✅ Success (View Logs) Apr 29, 2026 at 2:11 pm
archmax_standalone ✅ Success (View Logs) Apr 29, 2026 at 2:11 pm

@railway-app railway-app Bot temporarily deployed to archmax SemLayer / archmax-pr-48 April 29, 2026 14:09 Destroyed
@tobias-gp tobias-gp merged commit 6ffa5be into main Apr 29, 2026
5 of 6 checks passed
@github-actions
Copy link
Copy Markdown

Docker image ready

docker pull ghcr.io/archmaxai/archmax:pr-48

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant