CSRF Without Tokens: Cross-Origin Protection in Fiber v3.6
Most CSRF bugs I have seen in Fiber apps were not attacks. They were users who
left a form open over lunch, came back, clicked Save and got a 403 because the
token in the page had expired. Or an HTMX page where one fragment was rendered
without the hx-headers attribute that carries the token. Or a SPA that read
the token cookie on startup and kept sending a stale value after the session
rotated. The token itself was never the hard part. The plumbing was: every
template, every fetch wrapper and every cached page has to carry a value from
the server back to the server, and a single gap turns into a support ticket.
Browsers can tell a server where a request comes from. Every major browser
engine now sends the Sec-Fetch-Site header, and Go 1.25 used it to add
http.CrossOriginProtection to the standard library, a CSRF defense without any
token. Fiber v3.6.0
brings the same check to Fiber as
csrf.NewCrossOriginProtection
(#4596). It sets no cookie, needs
no storage and puts nothing in your templates.
This post protects a small notes service that has three kinds of clients: its own pages, a dashboard on a sibling subdomain, and an identity provider that posts back after login. We will exempt the one route that has to accept cross-site posts, roll the check out in report-only mode next to the existing tokens, pin the policy down in a test, and look at the cases where a token remains the better tool. Everything was built and tested against Fiber v3.6.1. The middleware needs v3.6.0 or later, which in turn requires Go 1.26.
