Skip to content

security: harden CSRF Origin check and CORS credentials handling (#5, #16)#585

Merged
lakhansamani merged 1 commit intomainfrom
security/csrf-cors-hardening
Apr 7, 2026
Merged

security: harden CSRF Origin check and CORS credentials handling (#5, #16)#585
lakhansamani merged 1 commit intomainfrom
security/csrf-cors-hardening

Conversation

@lakhansamani
Copy link
Copy Markdown
Contributor

Summary

Two related issues:

#5 — CSRF bypass with wildcard AllowedOrigins
The CSRF middleware called validators.IsValidOrigin to check the Origin header, but IsValidOrigin returns true for ANY origin when AllowedOrigins == ["*"] (the default). It also skipped the check entirely when Origin was missing, accepting an X-Requested-With trick. Combined, an attacker could send a state-changing POST from a malicious page without Origin and pass.

#16 — CORS wildcard with credentials
The CORS middleware already correctly avoids setting Access-Control-Allow-Credentials when origin is * or empty, so credentialed cross-origin requests aren't actually permitted in wildcard mode. Operators can still misread the default as "credentialed wildcard works" and rely on it.

Changes

  • CSRF: require Origin OR Referer on every state-changing method. Reject with 403 Origin or Referer header is required if both are missing.
  • New csrfOriginAllowed() validates the origin. When AllowedOrigins == ["*"], it falls back to same-origin enforcement (origin host must equal request host) — wildcard CORS does not mean wildcard CSRF. With an explicit allowlist it defers to IsValidOrigin.
  • The custom-header check (application/json or X-Requested-With) is still required as a second layer.
  • CORS: log a clear startup warning when AllowedOrigins contains * recommending an explicit allowlist for production.

Test plan

  • go build ./...
  • TEST_DBS=sqlite go test ./internal/integration_tests/
  • Manual: POST without Origin/Referer is rejected
  • Manual: POST with Origin matching Host (wildcard mode) is accepted
  • Manual: POST with foreign Origin (wildcard mode) is rejected

…16)

Two related issues:

#5 — CSRF bypass with wildcard AllowedOrigins
The CSRF middleware called validators.IsValidOrigin to check the Origin
header, but IsValidOrigin returns true for ANY origin when AllowedOrigins
is the wildcard ["*"] (the default). It also skipped the check entirely
when the Origin header was missing, accepting an X-Requested-With trick.
An attacker could send a state-changing POST from a malicious page
without Origin and pass the check.

Fix:
- Require Origin OR Referer on every state-changing method. Reject 403
  with "Origin or Referer header is required" if both are missing.
- A CSRF-specific csrfOriginAllowed() validates the origin. When
  AllowedOrigins == ["*"], it falls back to same-origin enforcement
  (Origin host must equal request Host) — wildcard CORS does not mean
  wildcard CSRF. With an explicit allowlist it defers to IsValidOrigin.
- The custom-header check (application/json or X-Requested-With) is
  still required as a second layer.

#16 — CORS wildcard with credentials
The CORS middleware already correctly avoids setting
Access-Control-Allow-Credentials when origin is "*" or empty, so
credentialed cross-origin requests are not actually permitted in
wildcard mode. To stop operators from accidentally relying on this
default in production, log a clear startup warning when AllowedOrigins
contains "*", recommending an explicit allowlist.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant