gorilla/csrf
gorilla/csrf is a HTTP middleware library that provides cross-site request
forgery (CSRF)
protection. It includes:
- The
csrf.Protect
middleware/handler provides CSRF protection on routes
attached to a router or a sub-router. - A
csrf.Token
function that provides the token to pass into your response,
whether that be a HTML form or a JSON response body. - ... and a
csrf.TemplateField
helper that you can pass into your html/template
templates to replace a {{ .csrfField }}
template tag with a hidden input
field.
gorilla/csrf is designed to work with any Go web framework, including:
gorilla/csrf is also compatible with middleware 'helper' libraries like
Alice and Negroni.
Install
With a properly configured Go toolchain:
go get github.com/gorilla/csrf
Examples
gorilla/csrf is easy to use: add the middleware to individual handlers with
the below:
CSRF := csrf.Protect([]byte("32-byte-long-auth-key"))
http.HandlerFunc("/route", CSRF(YourHandler))
... and then collect the token with csrf.Token(r)
before passing it to the
template, JSON body or HTTP header (you pick!). gorilla/csrf inspects the form body
(first) and HTTP headers (second) on subsequent POST/PUT/PATCH/DELETE/etc. requests
for the token.
HTML Forms
Here's the common use-case: HTML forms you want to provide CSRF protection for,
in order to protect malicious POST requests being made:
package main
import (
"net/http"
"github.com/gorilla/csrf"
)
func main() {
r := mux.NewRouter()
r.HandleFunc("/signup", ShowSignupForm)
r.HandleFunc("/signup/post", SubmitSignupForm)
http.ListenAndServe(":8000",
csrf.Protect([]byte("32-byte-long-auth-key"))(r))
}
func ShowSignupForm(w http.ResponseWriter, r *http.Request) {
t.ExecuteTemplate(w, "signup_form.tmpl", map[string]interface{
csrf.TemplateTag: csrf.TemplateField(r),
})
}
func SubmitSignupForm(w http.ResponseWriter, r *http.Request) {
}
JSON Responses
This approach is useful if you're using a front-end JavaScript framework like
Ember or Angular, or are providing a JSON API.
We'll also look at applying selective CSRF protection using
gorilla/mux's sub-routers,
as we don't handle any POST/PUT/DELETE requests with our top-level router.
package main
import (
"github.com/gorilla/csrf"
"github.com/gorilla/mux"
)
func main() {
r := mux.NewRouter()
api := r.PathPrefix("/api").Subrouter()
api.HandleFunc("/user/:id", GetUser).Methods("GET")
http.ListenAndServe(":8000",
csrf.Protect([]byte("32-byte-long-auth-key"))(r))
}
func GetUser(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-CSRF-Token", csrf.Token(r))
b, err := json.Marshal(user)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
w.Write(b)
}
Setting Options
What about providing your own error handler and changing the HTTP header the
package inspects on requests? (i.e. an existing API you're porting to Go). Well,
gorilla/csrf provides options for changing these as you see fit:
func main() {
CSRF := csrf.Protect(
[]byte("a-32-byte-long-key-goes-here"),
csrf.RequestHeader("Authenticity-Token"),
csrf.FieldName("authenticity_token"),
csrf.ErrorHandler(http.HandlerFunc(serverError(403))),
)
r := mux.NewRouter()
r.HandleFunc("/signup", GetSignupForm)
r.HandleFunc("/signup/post", PostSignupForm)
http.ListenAndServe(":8000", CSRF(r))
}
Not too bad, right?
If there's something you're confused about or a feature you would like to see
added, open an issue.
Design Notes
Getting CSRF protection right is important, so here's some background:
- This library generates unique-per-request (masked) tokens as a mitigation
against the BREACH attack.
- The 'base' (unmasked) token is stored in the session, which means that
multiple browser tabs won't cause a user problems as their per-request token
is compared with the base token.
- Operates on a "whitelist only" approach where safe (non-mutating) HTTP methods
(GET, HEAD, OPTIONS, TRACE) are the only methods where token validation is not
enforced.
- The design is based on the battle-tested
Django and Ruby on
Rails
approaches.
- Cookies are authenticated and based on the securecookie
library. They're also Secure (issued over HTTPS only) and are HttpOnly
by default, because sane defaults are important.
- Go's
crypto/rand
library is used to generate the 32 byte (256 bit) tokens
and the one-time-pad used for masking them.
This library does not seek to be adventurous.
License
BSD licensed. See the LICENSE file for details.