Skip to content

Replace hand-rolled CSRF tokens with http.CrossOriginProtection#45

Merged
umputun merged 1 commit into
umputun:masterfrom
paskal:feat/csrf-stdlib
Apr 17, 2026
Merged

Replace hand-rolled CSRF tokens with http.CrossOriginProtection#45
umputun merged 1 commit into
umputun:masterfrom
paskal:feat/csrf-stdlib

Conversation

@paskal
Copy link
Copy Markdown
Contributor

@paskal paskal commented Apr 17, 2026

Drops the per-request CSRF token cookie + hidden form field in favour of Go 1.25's http.CrossOriginProtection middleware (one line in server/server.go). The middleware checks the browser-set Sec-Fetch-Site header with an Origin vs Host fallback for older clients, so no client-side instrumentation is needed.

Why

Sec-Fetch-Site is a forbidden header -- the browser sets it and JS cannot forge it. It's been shipped in all major browsers since 2023 (96%+ global coverage per Can I Use), and OWASP elevated it from defence-in-depth to a primary defence in its CSRF cheatsheet in December 2025. Compared to the hand-rolled double-submit token it replaces:

  • no template instrumentation (CSRFToken field, hidden <input>)
  • no cookie management (csrf_token set/clear lifecycle, 5-min TTL)
  • no risk of stale-token UX bugs across tabs / back button
  • distinguishes same-origin from same-site, so subdomain attacks (which SameSite=Lax permits) are blocked

What changed

  • server/server.go -- router.Use(http.NewCrossOriginProtection().Handler) added to the existing middleware chain
  • server/auth.go -- removed generateCSRFToken, CSRFToken template field, all csrf_token cookie lifecycle code in handleLoginPage / handleLoginSubmit / renderLoginError
  • server/templates/login.html -- dropped the hidden csrf_token input
  • server/auth_test.go -- new TestCrossOriginProtection covering same-origin / cross-site / origin-host-mismatch / non-browser cases; obsolete "missing/invalid CSRF token" subtests removed
  • main_test.go -- integration tests no longer thread a CSRF token through the login flow

Net: ~190 LoC removed.

References for context

Notes

  • weblist already requires Go 1.26, so the stdlib middleware is available without a backport.
  • SameSite=Lax on the auth session cookie remains as defence-in-depth.
  • For TLS-terminating proxies the middleware compares Origin host against r.Host (HSTS recommended to prevent HTTP→HTTPS downgrades). The Sec-Fetch-Site path is checked first and is unaffected by proxy schemes.

Drop the per-request CSRF token cookie + hidden form field in favour
of Go 1.25's http.CrossOriginProtection middleware, which checks the
browser-set Sec-Fetch-Site header (with an Origin/Host fallback) and
needs no client-side instrumentation.

Net change: ~190 LoC removed.
@paskal paskal requested a review from umputun as a code owner April 17, 2026 01:20
Copy link
Copy Markdown
Owner

@umputun umputun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm - swapping 190 LoC of hand-rolled crypto/cookie plumbing for one stdlib line is a win, and the new middleware actually expands CSRF coverage: the old double-submit was only on /login, now /upload, /download-selected, /partials/selection-status are protected too.

@umputun umputun merged commit 3183dfe into umputun:master Apr 17, 2026
2 checks passed
@paskal paskal deleted the feat/csrf-stdlib branch April 17, 2026 15:39
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.

2 participants