1package csrf 2 3import ( 4 "net/http" 5 "time" 6 7 "github.com/gorilla/securecookie" 8) 9 10// store represents the session storage used for CSRF tokens. 11type store interface { 12 // Get returns the real CSRF token from the store. 13 Get(*http.Request) ([]byte, error) 14 // Save stores the real CSRF token in the store and writes a 15 // cookie to the http.ResponseWriter. 16 // For non-cookie stores, the cookie should contain a unique (256 bit) ID 17 // or key that references the token in the backend store. 18 // csrf.GenerateRandomBytes is a helper function for generating secure IDs. 19 Save(token []byte, w http.ResponseWriter) error 20} 21 22// cookieStore is a signed cookie session store for CSRF tokens. 23type cookieStore struct { 24 name string 25 maxAge int 26 secure bool 27 httpOnly bool 28 path string 29 domain string 30 sc *securecookie.SecureCookie 31} 32 33// Get retrieves a CSRF token from the session cookie. It returns an empty token 34// if decoding fails (e.g. HMAC validation fails or the named cookie doesn't exist). 35func (cs *cookieStore) Get(r *http.Request) ([]byte, error) { 36 // Retrieve the cookie from the request 37 cookie, err := r.Cookie(cs.name) 38 if err != nil { 39 return nil, err 40 } 41 42 token := make([]byte, tokenLength) 43 // Decode the HMAC authenticated cookie. 44 err = cs.sc.Decode(cs.name, cookie.Value, &token) 45 if err != nil { 46 return nil, err 47 } 48 49 return token, nil 50} 51 52// Save stores the CSRF token in the session cookie. 53func (cs *cookieStore) Save(token []byte, w http.ResponseWriter) error { 54 // Generate an encoded cookie value with the CSRF token. 55 encoded, err := cs.sc.Encode(cs.name, token) 56 if err != nil { 57 return err 58 } 59 60 cookie := &http.Cookie{ 61 Name: cs.name, 62 Value: encoded, 63 MaxAge: cs.maxAge, 64 HttpOnly: cs.httpOnly, 65 Secure: cs.secure, 66 Path: cs.path, 67 Domain: cs.domain, 68 } 69 70 // Set the Expires field on the cookie based on the MaxAge 71 // If MaxAge <= 0, we don't set the Expires attribute, making the cookie 72 // session-only. 73 if cs.maxAge > 0 { 74 cookie.Expires = time.Now().Add( 75 time.Duration(cs.maxAge) * time.Second) 76 } 77 78 // Write the authenticated cookie to the response. 79 http.SetCookie(w, cookie) 80 81 return nil 82} 83