CSRF Protection #
Build a secure Go web server without external dependencies — just the standard library.
In this short example, you’ll learn how to protect your web server from
Cross-Site request forgery (CSRF)
attacks. We'll be using the newly introduced
http.NewCrossOriginProtection()
module. By the end, you’ll know how to implement CSRF protection.
Unsecure Banking #
Imagine you:
- Are logged in to your bank account in your browser
- Click on an offer link from your bank in your email inbox
The link redirects you to your bank's website, but the offer page turns out to be dead.
A week later, you find out that money was sent to another bank account that you don't know. This happened because the link was malicious.
The Relieve #
We can protect our users by rejecting non-safe cross-origin browser
requests. The http.NewCrossOriginProtection() will help us do
this. It detects cross-origin requests in the following ways:
1
2
- By checking the Sec-Fetch-Site header if it's present.
- By comparing the hostname in the Origin header to the one in the Host header.
Import the Package #
To enable this protection, we have to import the required package:
import "net/http"
Setup the CSRF Protection #
We can initialize the http.NewCrossOriginProtection() and wrap
our Multiplexer (mux) in it. If you don't know how to setup a
simple web server, please take a look at this
article.
antiCSRF := http.NewCrossOriginProtection()
handler := antiCSRF.Handler(mux)
We can also add other trusted origins with
antiCSRF.AddTrustedOrigin("https://example.com"). The CSRF
protection will now allow requests coming from
https://example.com.
Complete Example #
package main
import (
"errors"
"fmt"
"net/http"
"os"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("POST /{$}", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, I'm protected against CSRF attacks!")
})
antiCSRF := http.NewCrossOriginProtection()
srv := &http.Server{
Addr: fmt.Sprintf(":%d", 8080),
Handler: antiCSRF.Handler(mux),
}
err := srv.ListenAndServe()
if !errors.Is(err, http.ErrServerClosed) {
os.Exit(1)
}
}
Try It with curl #
Once your server is running, you can test it using:
You should see this response:
Hello, I'm protected against CSRF attacks!
Let's try a different origin and host:
You should see this response:
cross-origin request detected, and/or browser is out of date: Sec-Fetch-Site is missing, and Origin does not match Host
The server did reject the host because the Origin header didn't match the Host.
Contributions
Special thanks to Patrick Henry Winston his book Make It Clear was instrumental in shaping this article. Looking ahead, I’ll be focusing on two projects: Beago, a Go framework for building LLM-powered applications, and Vona, a minimalist and lightweight starter kit that that utilises Pico for beautiful plug-and-play landing page UI blocks.