// TemplateField is a template helper for html/template that provides an <input> field // populated with a CSRF token. // // Example: // // // The following tag in our form.tmpl template: // {{ .csrfField }} // // // ... becomes: // <input type="hidden" name="gorilla.csrf.Token" value="<token>"> // func TemplateField(r *http.Request) template.HTML { name, ok := context.GetOk(r, FormKey) if ok { fragment := fmt.Sprintf(`<input type="hidden" name="%s" value="%s">`, name, Token(r)) return template.HTML(fragment) } return template.HTML("") }
// Protect is HTTP middleware that provides Cross-Site Request Forgery // protection. // // It securely generates a masked (unique-per-request) token that // can be embedded in the HTTP response (e.g. form field or HTTP header). // The original (unmasked) token is stored in the session, which is inaccessible // by an attacker (provided you are using HTTPS). Subsequent requests are // expected to include this token, which is compared against the session token. // Requests that do not provide a matching token are served with a HTTP 403 // 'Forbidden' error response. func csrfProtectHTTP(ctx *Context) bool { // No CSRF protect if mCsrfProtection == nil { return true } if v, _ := ctx.GetBool(_IS_CSRF_PROTECTED); v { return true } // Retrieve the token from the session. // An error represents either a cookie that failed HMAC validation // or that doesn't exist. r := ctx.r w := ctx.w realToken, err := mCsrfProtection.St.Get(r) if err != nil || len(realToken) != csrf.TokenLength { // If there was an error retrieving the token, the token doesn't exist // yet, or it's the wrong length, generate a new token. // Note that the new token will (correctly) fail validation downstream // as it will no longer match the request token. realToken, err = csrf.GenerateRandomBytes(csrf.TokenLength) if err != nil { csrf.EnvError(r, err) mCsrfProtection.Opts.ErrorHandler.ServeHTTP(w, r) return false } // Save the new (real) token in the session store. err = mCsrfProtection.St.Save(realToken, w) if err != nil { csrf.EnvError(r, err) mCsrfProtection.Opts.ErrorHandler.ServeHTTP(w, r) return false } } csrfToken := csrf.Mask(realToken, r) ctx.ViewData["csrfKey"] = csrf.TokenKey ctx.ViewData["csrfToken"] = csrfToken ctx.ViewData[csrf.TemplateTag] = template.HTML(fmt.Sprintf(`<input type="hidden" name="%s" value="%s">`, csrf.TokenKey, csrfToken)) // HTTP methods not defined as idempotent ("safe") under RFC7231 require // inspection. if !csrf.Contains(csrf.SafeMethods, r.Method) { // Enforce an origin check for HTTPS connections. As per the Django CSRF // implementation (https://goo.gl/vKA7GE) the Referer header is almost // always present for same-domain HTTP requests. if r.URL.Scheme == "https" { // Fetch the Referer value. Call the error handler if it's empty or // otherwise fails to parse. referer, err := url.Parse(r.Referer()) if err != nil || referer.String() == "" { csrf.EnvError(r, csrf.ErrNoReferer) mCsrfProtection.Opts.ErrorHandler.ServeHTTP(w, r) return false } if csrf.SameOrigin(r.URL, referer) == false { csrf.EnvError(r, csrf.ErrBadReferer) mCsrfProtection.Opts.ErrorHandler.ServeHTTP(w, r) return false } } // If the token returned from the session store is nil for non-idempotent // ("unsafe") methods, call the error handler. if realToken == nil { csrf.EnvError(r, csrf.ErrNoToken) mCsrfProtection.Opts.ErrorHandler.ServeHTTP(w, r) return false } // Retrieve the combined token (pad + masked) token and unmask it. requestToken := csrf.Unmask(mCsrfProtection.RequestToken(r)) // Compare the request token against the real token if !csrf.CompareTokens(requestToken, realToken) { csrf.EnvError(r, csrf.ErrBadToken) mCsrfProtection.Opts.ErrorHandler.ServeHTTP(w, r) return false } } // Set the Vary: Cookie header to protect clients from caching the response. w.Header().Add("Vary", "Cookie") ctx.Set(_IS_CSRF_PROTECTED, true) return true }