๐ค csrf
amadan.net/rastrillo/rastrillo/csrf
Same-origin enforcement for state-changing requests. No tokens, nothing to thread through a template.
Protect
func Protect(origin string) func(http.Handler) http.Handler
Middleware refusing cross-origin POST, PUT, PATCH and DELETE.
Mount it app-wide, above your route groups:
r := chi.NewRouter()
r.Use(csrf.Protect(origin))
Mounting it once at the top is what makes a route you add six months from now protected by default, instead of protected by someone remembering.
Safe methods pass through. GET and HEAD are not state-changing, and
an app that mutates on GET has a different problem this package cannot
fix.
SameOrigin
func SameOrigin(r *http.Request, origin string) bool
The predicate Protect applies, exported for a handler that needs the
same judgement outside the middleware.
Evidence is checked in order of quality. Sec-Fetch-Site first: the
browser sets it, page script cannot forge it, and it is unambiguous.
Then Origin, sent on cross-origin state-changing requests and reliable
when present. Then Referer, for the shrinking set of clients sending
neither.
Why no tokens
A synchroniser token has to be minted, stored, embedded in every form and validated โ four places to get it wrong, and the failure mode is a form that mysteriously rejects a legitimate submission.
The header check needs none of that and is stronger against the attacks
that matter, because Sec-Fetch-Site cannot be set by the page mounting
the attack. The cost is that a client sending none of the three headers
on a state-changing request is refused, which for an app served to
browsers is the right answer anyway.
Read this page as markdown โ exact, unstyled, and cheap for an agent to fetch.