Clearing `_gorilla_csrf` cookie not regenerating
donaldthai opened this issue · comments
Hi there, I'm having trouble with the secure cookie _gorilla_csrf
regenerating when I clear out the cookie from browser. I notice it only regenerates after a POST which would obviously fail and return a 403 error. I also notice that the cookie doesn't get set on page load as well.
More context:
- Working with a Single Page App, React
- Using Goji for handling multiple routes
- Passing in
csrf.Protect()
middleware to Goji at the root route
Any idea what's going on?
Any help would be appreciated!
See https://github.com/gorilla/csrf#javascript-applications
In an SPA you'll likely want to pre-fetch the cookie and the token behind the scenes, and then pass the token on every POST request.
Do you have any examples of pre-fetching the _gorilla_csrf
cookie? The only examples I see are the ones for the masked csrf token.
Not sure what I'm doing wrong. I've set the the path to csrf.Path('/')
which I would think would cover everything. Still not getting the cookie when I do a GET request for just '/'.
I do get the _gorilla_csrf
cookie on a failed POST like what I mentioned before.
Make sure the Path attribute of the cookie is broad enough to be sent on all requests in return
Here's a simplified version. It's initially what I'm doing:
Server
site := goji.NewMux()
// handling static content
// Goji pat library, "goji.io/pat"
site.Handle(pat.Get("/static/*"), clientHandler)
root := goji.NewMux()
// testing on localhost
root.Use(csrf.Protect(csrfAuthKey, csrf.Secure(false), csrf.Path("/")))
// writing "masked_gorilla_csrf" cookie
root.Use(MaskedCsrfCookieMiddleware())
site.Handle(pat.New("/*"), root)
// using "net/http" package
server := &http.Server{
Addr: addr("locahost", 8080),
Handler: site,
}
// go routine
go func() {
server.ListenAndServe()
}()
Middleware to write the masked token to cookie
func WriteMaskedCsrfCookie(w http.ResponseWriter, csrfToken string) {
// New cookie
cookie := http.Cookie{
Name: "masked_gorilla_csrf",
Value: "blank",
Path: "/",
Expires: time.Unix(0, 0),
MaxAge: -1,
}
// in mins
expiry := 15
maxAge := 15 * 60
cookie.Value = csrfToken
cookie.Expires = expiry
cookie.MaxAge = maxAge
w.Header().Set("Set-Cookie", cookie.String())
}
func MaskedCsrfCookieMiddleware() func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
mw := func(w http.ResponseWriter, r *http.Request) {
WriteMaskedCsrfCookie(w, csrf.Token(r))
next.ServeHTTP(w, r)
}
return http.HandlerFunc(mw)
}
}
Tried both browser navigation to "localhost:8080" and javascript fetch:
Fetch code
fetch("http://localhost:8080/", {
"method": "GET",
});
Currently, I'm writing another cookie named masked_gorilla_csrf
to pass back the masked token from csrf.Token(). I then retrieve that cookie and feed it into all of my requests by setting the X-CSRF-Token request header.
No issues getting the masked token. Just having trouble with the _gorilla_csrf
cookie.
GET request
For this one, I already had the masked_gorilla_csrf from a previous GET request.
GET / HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36
DNT: 1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: masked_gorilla_csrf=/mXggd3LDD9Nb+DqP77i2xPel/2bcHIjfUMciGVfJShAw0Apn2pvxkrpHU9g79XFIDBbqA/SMQUzv6h/LyWuWg==
GET Response:
HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Length: 604
Content-Security-Policy: frame-ancestors 'none'
Content-Type: text/html; charset=utf-8
Last-Modified: Tue, 11 Dec 2018 02:51:27 GMT
Set-Cookie: masked_gorilla_csrf=U/JffY5lf1AJJnSvhMF8bguwm8Xf8g0/4gmslRGDQXj6IzeJgUmdVMSCqSFTwvnq9h1LGsyW79BW8xTB4hCf3A==; Path=/; Expires=Fri, 01 Jan 9999 12:00:00 GMT; Max-Age=99999999
Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
Vary: Cookie
X-Frame-Options: deny
X-Xss-Protection: 1; mode=block
Date: Tue, 11 Dec 2018 19:04:47 GMT
POST Request
POST / HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 0
Pragma: no-cache
Cache-Control: no-cache
Origin: http://localhost:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36
DNT: 1
Accept: */*
Referer: http://localhost:8080/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: masked_gorilla_csrf=Z/tAJoao4DCNLwbchoheqD+LTqTeJiV2dIMyyOcQ5s40AxCeW4Izs3EOXuaEUTzNWT5kirqXaDeu9KIxISbF2Q==
POST response
HTTP/1.1 403 Forbidden
Content-Security-Policy: frame-ancestors 'none'
Content-Type: text/plain; charset=utf-8
Set-Cookie: session_token=blank; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0
Set-Cookie: _gorilla_csrf=MTU0NDU1NTIxNnxJbFZoYmxabk1sTjBkMWR3ZFhwcVpFWk9PVlk1VVZOcU5ESnBkbkpoZVhkTGEwbFRjME55ZG5wUE1UQTlJZ289fPJs9mjuG4KS8ARaoeRdVQatoXGG_ABfhcigcg8QZLj9; Path=/; HttpOnly
Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
X-Content-Type-Options: nosniff
X-Frame-Options: deny
X-Xss-Protection: 1; mode=block
Date: Tue, 11 Dec 2018 19:06:56 GMT
Content-Length: 31
Quick note, I just made a POST request to the "/" that hasn't been setup for a POST.
Hey! Just a quick update. Was able to figure out what was wrong. Had to add the CSRF middleware before writing the masked_gorilla_cookie
and set it after it.
Updated Server
site := goji.NewMux()
// handling static content
// Goji pat library, "goji.io/pat"
site.Handle(pat.Get("/static/*"), clientHandler)
root := goji.NewMux()
// testing on localhost
UnmaskedCsrfCookieMiddleware := csrf.Protect(csrfAuthKey, csrf.Secure(false), csrf.Path("/"))
root.Use(UnmaskedCsrfCookieMiddleware)
// writing "masked_gorilla_csrf" cookie
root.Use(MaskedCsrfCookieMiddleware())
root.Use(UnmaskedCsrfCookieMiddleware)
site.Handle(pat.New("/*"), root)
// using "net/http" package
server := &http.Server{
Addr: addr("locahost", 8080),
Handler: site,
}
// go routine
go func() {
server.ListenAndServe()
}()
Another update! A colleague figured out that the _gorilla_csrf
cookie was being cleared when we set our masked_gorilla_csrf
cookie. This was because we were using w.Header().Set("Set-Cookie", cookie.String())
cleared all the cookies. Needed to use http.SetCookie
which uses w.Header().Add()
under the hood.
Updated code
Server
site := goji.NewMux()
// handling static content
// Goji pat library, "goji.io/pat"
site.Handle(pat.Get("/static/*"), clientHandler)
root := goji.NewMux()
// testing on localhost
root.Use(csrf.Protect(csrfAuthKey, csrf.Secure(false), csrf.Path("/")))
// writing "masked_gorilla_csrf" cookie
root.Use(MaskedCsrfCookieMiddleware())
site.Handle(pat.New("/*"), root)
// using "net/http" package
server := &http.Server{
Addr: addr("locahost", 8080),
Handler: site,
}
// go routine
go func() {
server.ListenAndServe()
}()
Middleware to write the masked token to cookie
func WriteMaskedCsrfCookie(w http.ResponseWriter, csrfToken string) {
// New cookie
cookie := http.Cookie{
Name: "masked_gorilla_csrf",
Value: "blank",
Path: "/",
Expires: time.Unix(0, 0),
MaxAge: -1,
}
// in mins
expiry := 15
maxAge := 15 * 60
cookie.Value = csrfToken
cookie.Expires = expiry
cookie.MaxAge = maxAge
http.SetCookie(w, &cookie)
}
func MaskedCsrfCookieMiddleware() func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
mw := func(w http.ResponseWriter, r *http.Request) {
WriteMaskedCsrfCookie(w, csrf.Token(r))
next.ServeHTTP(w, r)
}
return http.HandlerFunc(mw)
}
}
This issue has been automatically marked as stale because it hasn't seen a recent update. It'll be automatically closed in a few days.