diff options
author | Niall Sheridan <nsheridan@gmail.com> | 2016-07-17 23:54:42 +0100 |
---|---|---|
committer | Niall Sheridan <nsheridan@gmail.com> | 2016-07-24 23:23:33 +0100 |
commit | 44fef1c2a163bdfd781ef08a06e3cf5cf9b7d5da (patch) | |
tree | bcde234bf45255a8935aeacf7ee544f256b455cc /vendor/github.com/gorilla | |
parent | c9849d667ab55c23d343332a11afb3eb8ede3f2d (diff) |
Add a page for revoking certs
Add a template for revocation
Use DATETIME type to store created/expires times
Require auth for the /admin and /revoke endpoints
Diffstat (limited to 'vendor/github.com/gorilla')
-rw-r--r-- | vendor/github.com/gorilla/csrf/LICENSE | 27 | ||||
-rw-r--r-- | vendor/github.com/gorilla/csrf/README.md | 232 | ||||
-rw-r--r-- | vendor/github.com/gorilla/csrf/context.go | 25 | ||||
-rw-r--r-- | vendor/github.com/gorilla/csrf/context_legacy.go | 24 | ||||
-rw-r--r-- | vendor/github.com/gorilla/csrf/csrf.go | 276 | ||||
-rw-r--r-- | vendor/github.com/gorilla/csrf/doc.go | 159 | ||||
-rw-r--r-- | vendor/github.com/gorilla/csrf/helpers.go | 205 | ||||
-rw-r--r-- | vendor/github.com/gorilla/csrf/options.go | 130 | ||||
-rw-r--r-- | vendor/github.com/gorilla/csrf/store.go | 82 |
9 files changed, 1160 insertions, 0 deletions
diff --git a/vendor/github.com/gorilla/csrf/LICENSE b/vendor/github.com/gorilla/csrf/LICENSE new file mode 100644 index 0000000..f407b7f --- /dev/null +++ b/vendor/github.com/gorilla/csrf/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2015, Matt Silverlock (matt@eatsleeprepeat.net) All rights +reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/gorilla/csrf/README.md b/vendor/github.com/gorilla/csrf/README.md new file mode 100644 index 0000000..02e4f42 --- /dev/null +++ b/vendor/github.com/gorilla/csrf/README.md @@ -0,0 +1,232 @@ +# gorilla/csrf +[![GoDoc](https://godoc.org/github.com/gorilla/csrf?status.svg)](https://godoc.org/github.com/gorilla/csrf) [![Build Status](https://travis-ci.org/gorilla/csrf.svg?branch=master)](https://travis-ci.org/gorilla/csrf) + +gorilla/csrf is a HTTP middleware library that provides [cross-site request +forgery](http://blog.codinghorror.com/preventing-csrf-and-xsrf-attacks/) (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: + +* The [Gorilla](http://www.gorillatoolkit.org/) toolkit +* Go's built-in [net/http](http://golang.org/pkg/net/http/) package +* [Goji](https://goji.io) - see the [tailored fork](https://github.com/goji/csrf) +* [Gin](https://github.com/gin-gonic/gin) +* [Echo](https://github.com/labstack/echo) +* ... and any other router/framework that rallies around Go's `http.Handler` interface. + +gorilla/csrf is also compatible with middleware 'helper' libraries like +[Alice](https://github.com/justinas/alice) and [Negroni](https://github.com/codegangsta/negroni). + +## Install + +With a properly configured Go toolchain: +```sh +go get github.com/gorilla/csrf +``` + +## Examples + +* [HTML Forms](#html-forms) +* [JavaScript Apps](#javascript-applications) +* [Google App Engine](#google-app-engine) +* [Setting Options](#setting-options) + +gorilla/csrf is easy to use: add the middleware to your router with +the below: + +```go +CSRF := csrf.Protect([]byte("32-byte-long-auth-key")) +http.ListenAndServe(":8000", CSRF(r)) +``` + +...and then collect the token with `csrf.Token(r)` in your handlers before +passing it to the template, JSON body or HTTP header (see below). + +Note that the authentication key passed to `csrf.Protect([]byte(key))` should be +32-bytes long and persist across application restarts. Generating a random key +won't allow you to authenticate existing cookies and will break your CSRF +validation. + +gorilla/csrf inspects the HTTP headers (first) and form body (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: + +```go +package main + +import ( + "net/http" + + "github.com/gorilla/csrf" + "github.com/gorilla/mux" +) + +func main() { + r := mux.NewRouter() + r.HandleFunc("/signup", ShowSignupForm) + // All POST requests without a valid token will return HTTP 403 Forbidden. + r.HandleFunc("/signup/post", SubmitSignupForm) + + // Add the middleware to your router by wrapping it. + http.ListenAndServe(":8000", + csrf.Protect([]byte("32-byte-long-auth-key"))(r)) + // PS: Don't forget to pass csrf.Secure(false) if you're developing locally + // over plain HTTP (just don't leave it on in production). +} + +func ShowSignupForm(w http.ResponseWriter, r *http.Request) { + // signup_form.tmpl just needs a {{ .csrfField }} template tag for + // csrf.TemplateField to inject the CSRF token into. Easy! + t.ExecuteTemplate(w, "signup_form.tmpl", map[string]interface{ + csrf.TemplateTag: csrf.TemplateField(r), + }) + // We could also retrieve the token directly from csrf.Token(r) and + // set it in the request header - w.Header.Set("X-CSRF-Token", token) + // This is useful if your sending JSON to clients or a front-end JavaScript + // framework. +} + +func SubmitSignupForm(w http.ResponseWriter, r *http.Request) { + // We can trust that requests making it this far have satisfied + // our CSRF protection requirements. +} +``` + +Note that the CSRF middleware will (by necessity) consume the request body if the +token is passed via POST form values. If you need to consume this in your +handler, insert your own middleware earlier in the chain to capture the request +body. + +### JavaScript Applications + +This approach is useful if you're using a front-end JavaScript framework like +React, Ember or Angular, or are providing a JSON API. + +We'll also look at applying selective CSRF protection using +[gorilla/mux's](http://www.gorillatoolkit.org/pkg/mux) sub-routers, +as we don't handle any POST/PUT/DELETE requests with our top-level router. + +```go +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) { + // Authenticate the request, get the id from the route params, + // and fetch the user from the DB, etc. + + // Get the token and pass it in the CSRF header. Our JSON-speaking client + // or JavaScript framework can now read the header and return the token in + // in its own "X-CSRF-Token" request header on the subsequent POST. + 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) +} +``` + +### Google App Engine + +If you're using [Google App +Engine](https://cloud.google.com/appengine/docs/go/how-requests-are-handled#Go_Requests_and_HTTP), +which doesn't allow you to hook into the default `http.ServeMux` directly, +you can still use gorilla/csrf (and gorilla/mux): + +```go +package app + +// Remember: appengine has its own package main +func init() { + r := mux.NewRouter() + r.HandleFunc("/", IndexHandler) + // ... + + // We pass our CSRF-protected router to the DefaultServeMux + http.Handle("/", csrf.Protect([]byte(your-key))(r)) +} +``` + +### 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: + +```go +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](http://breachattack.com/). +* 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](https://docs.djangoproject.com/en/1.8/ref/csrf/) and [Ruby on + Rails](http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html) + approaches. +* Cookies are authenticated and based on the [securecookie](https://github.com/gorilla/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. diff --git a/vendor/github.com/gorilla/csrf/context.go b/vendor/github.com/gorilla/csrf/context.go new file mode 100644 index 0000000..fe47270 --- /dev/null +++ b/vendor/github.com/gorilla/csrf/context.go @@ -0,0 +1,25 @@ +// +build go1.7 + +package csrf + +import ( + "context" + "net/http" + + "github.com/pkg/errors" +) + +func contextGet(r *http.Request, key string) (interface{}, error) { + val := r.Context().Value(key) + if val == nil { + return nil, errors.Errorf("no value exists in the context for key %q", key) + } + + return val, nil +} + +func contextSave(r *http.Request, key string, val interface{}) *http.Request { + ctx := r.Context() + ctx = context.WithValue(ctx, key, val) + return r.WithContext(ctx) +} diff --git a/vendor/github.com/gorilla/csrf/context_legacy.go b/vendor/github.com/gorilla/csrf/context_legacy.go new file mode 100644 index 0000000..dabf0a6 --- /dev/null +++ b/vendor/github.com/gorilla/csrf/context_legacy.go @@ -0,0 +1,24 @@ +// +build !go1.7 + +package csrf + +import ( + "net/http" + + "github.com/gorilla/context" + + "github.com/pkg/errors" +) + +func contextGet(r *http.Request, key string) (interface{}, error) { + if val, ok := context.GetOk(r, key); ok { + return val, nil + } + + return nil, errors.Errorf("no value exists in the context for key %q", key) +} + +func contextSave(r *http.Request, key string, val interface{}) *http.Request { + context.Set(r, key, val) + return r +} diff --git a/vendor/github.com/gorilla/csrf/csrf.go b/vendor/github.com/gorilla/csrf/csrf.go new file mode 100644 index 0000000..dc4755e --- /dev/null +++ b/vendor/github.com/gorilla/csrf/csrf.go @@ -0,0 +1,276 @@ +package csrf + +import ( + "fmt" + "net/http" + "net/url" + + "github.com/pkg/errors" + + "github.com/gorilla/context" + "github.com/gorilla/securecookie" +) + +// CSRF token length in bytes. +const tokenLength = 32 + +// Context/session keys & prefixes +const ( + tokenKey string = "gorilla.csrf.Token" + formKey string = "gorilla.csrf.Form" + errorKey string = "gorilla.csrf.Error" + skipCheckKey string = "gorilla.csrf.Skip" + cookieName string = "_gorilla_csrf" + errorPrefix string = "gorilla/csrf: " +) + +var ( + // The name value used in form fields. + fieldName = tokenKey + // defaultAge sets the default MaxAge for cookies. + defaultAge = 3600 * 12 + // The default HTTP request header to inspect + headerName = "X-CSRF-Token" + // Idempotent (safe) methods as defined by RFC7231 section 4.2.2. + safeMethods = []string{"GET", "HEAD", "OPTIONS", "TRACE"} +) + +// TemplateTag provides a default template tag - e.g. {{ .csrfField }} - for use +// with the TemplateField function. +var TemplateTag = "csrfField" + +var ( + // ErrNoReferer is returned when a HTTPS request provides an empty Referer + // header. + ErrNoReferer = errors.New("referer not supplied") + // ErrBadReferer is returned when the scheme & host in the URL do not match + // the supplied Referer header. + ErrBadReferer = errors.New("referer invalid") + // ErrNoToken is returned if no CSRF token is supplied in the request. + ErrNoToken = errors.New("CSRF token not found in request") + // ErrBadToken is returned if the CSRF token in the request does not match + // the token in the session, or is otherwise malformed. + ErrBadToken = errors.New("CSRF token invalid") +) + +type csrf struct { + h http.Handler + sc *securecookie.SecureCookie + st store + opts options +} + +// options contains the optional settings for the CSRF middleware. +type options struct { + MaxAge int + Domain string + Path string + // Note that the function and field names match the case of the associated + // http.Cookie field instead of the "correct" HTTPOnly name that golint suggests. + HttpOnly bool + Secure bool + RequestHeader string + FieldName string + ErrorHandler http.Handler + CookieName string +} + +// 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. +// +// Example: +// package main +// +// import ( +// "github.com/elithrar/protect" +// "github.com/gorilla/mux" +// ) +// +// func main() { +// r := mux.NewRouter() +// +// mux.HandlerFunc("/signup", GetSignupForm) +// // POST requests without a valid token will return a HTTP 403 Forbidden. +// mux.HandlerFunc("/signup/post", PostSignupForm) +// +// // Add the middleware to your router. +// http.ListenAndServe(":8000", +// // Note that the authentication key provided should be 32 bytes +// // long and persist across application restarts. +// csrf.Protect([]byte("32-byte-long-auth-key"))(r)) +// } +// +// func GetSignupForm(w http.ResponseWriter, r *http.Request) { +// // signup_form.tmpl just needs a {{ .csrfField }} template tag for +// // csrf.TemplateField to inject the CSRF token into. Easy! +// t.ExecuteTemplate(w, "signup_form.tmpl", map[string]interface{ +// csrf.TemplateTag: csrf.TemplateField(r), +// }) +// // We could also retrieve the token directly from csrf.Token(r) and +// // set it in the request header - w.Header.Set("X-CSRF-Token", token) +// // This is useful if your sending JSON to clients or a front-end JavaScript +// // framework. +// } +// +func Protect(authKey []byte, opts ...Option) func(http.Handler) http.Handler { + return func(h http.Handler) http.Handler { + cs := parseOptions(h, opts...) + + // Set the defaults if no options have been specified + if cs.opts.ErrorHandler == nil { + cs.opts.ErrorHandler = http.HandlerFunc(unauthorizedHandler) + } + + if cs.opts.MaxAge < 0 { + // Default of 12 hours + cs.opts.MaxAge = defaultAge + } + + if cs.opts.FieldName == "" { + cs.opts.FieldName = fieldName + } + + if cs.opts.CookieName == "" { + cs.opts.CookieName = cookieName + } + + if cs.opts.RequestHeader == "" { + cs.opts.RequestHeader = headerName + } + + // Create an authenticated securecookie instance. + if cs.sc == nil { + cs.sc = securecookie.New(authKey, nil) + // Use JSON serialization (faster than one-off gob encoding) + cs.sc.SetSerializer(securecookie.JSONEncoder{}) + // Set the MaxAge of the underlying securecookie. + cs.sc.MaxAge(cs.opts.MaxAge) + } + + if cs.st == nil { + // Default to the cookieStore + cs.st = &cookieStore{ + name: cs.opts.CookieName, + maxAge: cs.opts.MaxAge, + secure: cs.opts.Secure, + httpOnly: cs.opts.HttpOnly, + path: cs.opts.Path, + domain: cs.opts.Domain, + sc: cs.sc, + } + } + + return cs + } +} + +// Implements http.Handler for the csrf type. +func (cs *csrf) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // Skip the check if directed to. This should always be a bool. + if val, err := contextGet(r, skipCheckKey); err == nil { + if skip, ok := val.(bool); ok { + if skip { + cs.h.ServeHTTP(w, r) + return + } + } + } + + // Retrieve the token from the session. + // An error represents either a cookie that failed HMAC validation + // or that doesn't exist. + realToken, err := cs.st.Get(r) + if err != nil || len(realToken) != 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 = generateRandomBytes(tokenLength) + if err != nil { + envError(r, err) + cs.opts.ErrorHandler.ServeHTTP(w, r) + return + } + + // Save the new (real) token in the session store. + err = cs.st.Save(realToken, w) + if err != nil { + envError(r, err) + cs.opts.ErrorHandler.ServeHTTP(w, r) + return + } + } + + // Save the masked token to the request context + r = contextSave(r, tokenKey, mask(realToken, r)) + // Save the field name to the request context + r = contextSave(r, formKey, cs.opts.FieldName) + + // HTTP methods not defined as idempotent ("safe") under RFC7231 require + // inspection. + if !contains(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() == "" { + envError(r, ErrNoReferer) + cs.opts.ErrorHandler.ServeHTTP(w, r) + return + } + + if sameOrigin(r.URL, referer) == false { + envError(r, ErrBadReferer) + cs.opts.ErrorHandler.ServeHTTP(w, r) + return + } + } + + // If the token returned from the session store is nil for non-idempotent + // ("unsafe") methods, call the error handler. + if realToken == nil { + envError(r, ErrNoToken) + cs.opts.ErrorHandler.ServeHTTP(w, r) + return + } + + // Retrieve the combined token (pad + masked) token and unmask it. + requestToken := unmask(cs.requestToken(r)) + + // Compare the request token against the real token + if !compareTokens(requestToken, realToken) { + envError(r, ErrBadToken) + cs.opts.ErrorHandler.ServeHTTP(w, r) + return + } + + } + + // Set the Vary: Cookie header to protect clients from caching the response. + w.Header().Add("Vary", "Cookie") + + // Call the wrapped handler/router on success. + cs.h.ServeHTTP(w, r) + // Clear the request context after the handler has completed. + context.Clear(r) +} + +// unauthorizedhandler sets a HTTP 403 Forbidden status and writes the +// CSRF failure reason to the response. +func unauthorizedHandler(w http.ResponseWriter, r *http.Request) { + http.Error(w, fmt.Sprintf("%s - %s", + http.StatusText(http.StatusForbidden), FailureReason(r)), + http.StatusForbidden) + return +} diff --git a/vendor/github.com/gorilla/csrf/doc.go b/vendor/github.com/gorilla/csrf/doc.go new file mode 100644 index 0000000..612c8d9 --- /dev/null +++ b/vendor/github.com/gorilla/csrf/doc.go @@ -0,0 +1,159 @@ +/* +Package csrf (gorilla/csrf) provides Cross Site Request Forgery (CSRF) +prevention middleware for Go web applications & services. + +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 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. + +Note that the authentication key passed to `csrf.Protect([]byte(key))` should be +32-bytes long and persist across application restarts. Generating a random key +won't allow you to authenticate existing cookies and will break your CSRF +validation. + +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 ( + "fmt" + "html/template" + "net/http" + + "github.com/gorilla/csrf" + "github.com/gorilla/mux" + ) + + var form = ` + <html> + <head> + <title>Sign Up!</title> + </head> + <body> + <form method="POST" action="/signup/post" accept-charset="UTF-8"> + <input type="text" name="name"> + <input type="text" name="email"> + <!-- + The default template tag used by the CSRF middleware . + This will be replaced with a hidden <input> field containing the + masked CSRF token. + --> + {{ .csrfField }} + <input type="submit" value="Sign up!"> + </form> + </body> + </html> + ` + + var t = template.Must(template.New("signup_form.tmpl").Parse(form)) + + func main() { + r := mux.NewRouter() + r.HandleFunc("/signup", ShowSignupForm) + // All POST requests without a valid token will return HTTP 403 Forbidden. + r.HandleFunc("/signup/post", SubmitSignupForm) + + // Add the middleware to your router by wrapping it. + http.ListenAndServe(":8000", + csrf.Protect([]byte("32-byte-long-auth-key"))(r)) + } + + func ShowSignupForm(w http.ResponseWriter, r *http.Request) { + // signup_form.tmpl just needs a {{ .csrfField }} template tag for + // csrf.TemplateField to inject the CSRF token into. Easy! + t.ExecuteTemplate(w, "signup_form.tmpl", map[string]interface{}{ + csrf.TemplateTag: csrf.TemplateField(r), + }) + } + + func SubmitSignupForm(w http.ResponseWriter, r *http.Request) { + // We can trust that requests making it this far have satisfied + // our CSRF protection requirements. + fmt.Fprintf(w, "%v\n", r.PostForm) + } + +Note that the CSRF middleware will (by necessity) consume the request body if the +token is passed via POST form values. If you need to consume this in your +handler, insert your own middleware earlier in the chain to capture the request +body. + +You can also send the CSRF token in the response header. This approach is useful +if you're using a front-end JavaScript framework like Ember or Angular, or are +providing a JSON API: + + 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) { + // Authenticate the request, get the id from the route params, + // and fetch the user from the DB, etc. + + // Get the token and pass it in the CSRF header. Our JSON-speaking client + // or JavaScript framework can now read the header and return the token in + // in its own "X-CSRF-Token" request header on the subsequent POST. + 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) + } + +In addition: 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](http://breachattack.com/). + * 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](https://docs.djangoproject.com/en/1.8/ref/csrf/) and [Ruby on + Rails](http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html) + approaches. + * Cookies are authenticated and based on the [securecookie](https://github.com/gorilla/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. + +*/ +package csrf diff --git a/vendor/github.com/gorilla/csrf/helpers.go b/vendor/github.com/gorilla/csrf/helpers.go new file mode 100644 index 0000000..7adb5ff --- /dev/null +++ b/vendor/github.com/gorilla/csrf/helpers.go @@ -0,0 +1,205 @@ +package csrf + +import ( + "crypto/rand" + "crypto/subtle" + "encoding/base64" + "fmt" + "html/template" + "net/http" + "net/url" + + "github.com/gorilla/context" +) + +// Token returns a masked CSRF token ready for passing into HTML template or +// a JSON response body. An empty token will be returned if the middleware +// has not been applied (which will fail subsequent validation). +func Token(r *http.Request) string { + if val, err := contextGet(r, tokenKey); err == nil { + if maskedToken, ok := val.(string); ok { + return maskedToken + } + } + + return "" +} + +// FailureReason makes CSRF validation errors available in the request context. +// This is useful when you want to log the cause of the error or report it to +// client. +func FailureReason(r *http.Request) error { + if val, err := contextGet(r, errorKey); err == nil { + if err, ok := val.(error); ok { + return err + } + } + + return nil +} + +// UnsafeSkipCheck will skip the CSRF check for any requests. This must be +// called before the CSRF middleware. +// +// Note: You should not set this without otherwise securing the request from +// CSRF attacks. The primary use-case for this function is to turn off CSRF +// checks for non-browser clients using authorization tokens against your API. +func UnsafeSkipCheck(r *http.Request) *http.Request { + return contextSave(r, skipCheckKey, true) +} + +// 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 { + if name, err := contextGet(r, formKey); err == nil { + fragment := fmt.Sprintf(`<input type="hidden" name="%s" value="%s">`, + name, Token(r)) + + return template.HTML(fragment) + } + + return template.HTML("") +} + +// mask returns a unique-per-request token to mitigate the BREACH attack +// as per http://breachattack.com/#mitigations +// +// The token is generated by XOR'ing a one-time-pad and the base (session) CSRF +// token and returning them together as a 64-byte slice. This effectively +// randomises the token on a per-request basis without breaking multiple browser +// tabs/windows. +func mask(realToken []byte, r *http.Request) string { + otp, err := generateRandomBytes(tokenLength) + if err != nil { + return "" + } + + // XOR the OTP with the real token to generate a masked token. Append the + // OTP to the front of the masked token to allow unmasking in the subsequent + // request. + return base64.StdEncoding.EncodeToString(append(otp, xorToken(otp, realToken)...)) +} + +// unmask splits the issued token (one-time-pad + masked token) and returns the +// unmasked request token for comparison. +func unmask(issued []byte) []byte { + // Issued tokens are always masked and combined with the pad. + if len(issued) != tokenLength*2 { + return nil + } + + // We now know the length of the byte slice. + otp := issued[tokenLength:] + masked := issued[:tokenLength] + + // Unmask the token by XOR'ing it against the OTP used to mask it. + return xorToken(otp, masked) +} + +// requestToken returns the issued token (pad + masked token) from the HTTP POST +// body or HTTP header. It will return nil if the token fails to decode. +func (cs *csrf) requestToken(r *http.Request) []byte { + // 1. Check the HTTP header first. + issued := r.Header.Get(cs.opts.RequestHeader) + + // 2. Fall back to the POST (form) value. + if issued == "" { + issued = r.PostFormValue(cs.opts.FieldName) + } + + // 3. Finally, fall back to the multipart form (if set). + if issued == "" && r.MultipartForm != nil { + vals := r.MultipartForm.Value[cs.opts.FieldName] + + if len(vals) > 0 { + issued = vals[0] + } + } + + // Decode the "issued" (pad + masked) token sent in the request. Return a + // nil byte slice on a decoding error (this will fail upstream). + decoded, err := base64.StdEncoding.DecodeString(issued) + if err != nil { + return nil + } + + return decoded +} + +// generateRandomBytes returns securely generated random bytes. +// It will return an error if the system's secure random number generator +// fails to function correctly. +func generateRandomBytes(n int) ([]byte, error) { + b := make([]byte, n) + _, err := rand.Read(b) + // err == nil only if len(b) == n + if err != nil { + return nil, err + } + + return b, nil + +} + +// sameOrigin returns true if URLs a and b share the same origin. The same +// origin is defined as host (which includes the port) and scheme. +func sameOrigin(a, b *url.URL) bool { + return (a.Scheme == b.Scheme && a.Host == b.Host) +} + +// compare securely (constant-time) compares the unmasked token from the request +// against the real token from the session. +func compareTokens(a, b []byte) bool { + // This is required as subtle.ConstantTimeCompare does not check for equal + // lengths in Go versions prior to 1.3. + if len(a) != len(b) { + return false + } + + return subtle.ConstantTimeCompare(a, b) == 1 +} + +// xorToken XORs tokens ([]byte) to provide unique-per-request CSRF tokens. It +// will return a masked token if the base token is XOR'ed with a one-time-pad. +// An unmasked token will be returned if a masked token is XOR'ed with the +// one-time-pad used to mask it. +func xorToken(a, b []byte) []byte { + n := len(a) + if len(b) < n { + n = len(b) + } + + res := make([]byte, n) + + for i := 0; i < n; i++ { + res[i] = a[i] ^ b[i] + } + + return res +} + +// contains is a helper function to check if a string exists in a slice - e.g. +// whether a HTTP method exists in a list of safe methods. +func contains(vals []string, s string) bool { + for _, v := range vals { + if v == s { + return true + } + } + + return false +} + +// envError stores a CSRF error in the request context. +func envError(r *http.Request, err error) { + context.Set(r, errorKey, err) +} diff --git a/vendor/github.com/gorilla/csrf/options.go b/vendor/github.com/gorilla/csrf/options.go new file mode 100644 index 0000000..c644d49 --- /dev/null +++ b/vendor/github.com/gorilla/csrf/options.go @@ -0,0 +1,130 @@ +package csrf + +import "net/http" + +// Option describes a functional option for configuring the CSRF handler. +type Option func(*csrf) + +// MaxAge sets the maximum age (in seconds) of a CSRF token's underlying cookie. +// Defaults to 12 hours. +func MaxAge(age int) Option { + return func(cs *csrf) { + cs.opts.MaxAge = age + } +} + +// Domain sets the cookie domain. Defaults to the current domain of the request +// only (recommended). +// +// This should be a hostname and not a URL. If set, the domain is treated as +// being prefixed with a '.' - e.g. "example.com" becomes ".example.com" and +// matches "www.example.com" and "secure.example.com". +func Domain(domain string) Option { + return func(cs *csrf) { + cs.opts.Domain = domain + } +} + +// Path sets the cookie path. Defaults to the path the cookie was issued from +// (recommended). +// +// This instructs clients to only respond with cookie for that path and its +// subpaths - i.e. a cookie issued from "/register" would be included in requests +// to "/register/step2" and "/register/submit". +func Path(p string) Option { + return func(cs *csrf) { + cs.opts.Path = p + } +} + +// Secure sets the 'Secure' flag on the cookie. Defaults to true (recommended). +// Set this to 'false' in your development environment otherwise the cookie won't +// be sent over an insecure channel. Setting this via the presence of a 'DEV' +// environmental variable is a good way of making sure this won't make it to a +// production environment. +func Secure(s bool) Option { + return func(cs *csrf) { + cs.opts.Secure = s + } +} + +// HttpOnly sets the 'HttpOnly' flag on the cookie. Defaults to true (recommended). +func HttpOnly(h bool) Option { + return func(cs *csrf) { + // Note that the function and field names match the case of the + // related http.Cookie field instead of the "correct" HTTPOnly name + // that golint suggests. + cs.opts.HttpOnly = h + } +} + +// ErrorHandler allows you to change the handler called when CSRF request +// processing encounters an invalid token or request. A typical use would be to +// provide a handler that returns a static HTML file with a HTTP 403 status. By +// default a HTTP 403 status and a plain text CSRF failure reason are served. +// +// Note that a custom error handler can also access the csrf.Failure(r) +// function to retrieve the CSRF validation reason from the request context. +func ErrorHandler(h http.Handler) Option { + return func(cs *csrf) { + cs.opts.ErrorHandler = h + } +} + +// RequestHeader allows you to change the request header the CSRF middleware +// inspects. The default is X-CSRF-Token. +func RequestHeader(header string) Option { + return func(cs *csrf) { + cs.opts.RequestHeader = header + } +} + +// FieldName allows you to change the name attribute of the hidden <input> field +// inspected by this package. The default is 'gorilla.csrf.Token'. +func FieldName(name string) Option { + return func(cs *csrf) { + cs.opts.FieldName = name + } +} + +// CookieName changes the name of the CSRF cookie issued to clients. +// +// Note that cookie names should not contain whitespace, commas, semicolons, +// backslashes or control characters as per RFC6265. +func CookieName(name string) Option { + return func(cs *csrf) { + cs.opts.CookieName = name + } +} + +// setStore sets the store used by the CSRF middleware. +// Note: this is private (for now) to allow for internal API changes. +func setStore(s store) Option { + return func(cs *csrf) { + cs.st = s + } +} + +// parseOptions parses the supplied options functions and returns a configured +// csrf handler. +func parseOptions(h http.Handler, opts ...Option) *csrf { + // Set the handler to call after processing. + cs := &csrf{ + h: h, + } + + // Default to true. See Secure & HttpOnly function comments for rationale. + // Set here to allow package users to override the default. + cs.opts.Secure = true + cs.opts.HttpOnly = true + + // Range over each options function and apply it + // to our csrf type to configure it. Options functions are + // applied in order, with any conflicting options overriding + // earlier calls. + for _, option := range opts { + option(cs) + } + + return cs +} diff --git a/vendor/github.com/gorilla/csrf/store.go b/vendor/github.com/gorilla/csrf/store.go new file mode 100644 index 0000000..39f47ad --- /dev/null +++ b/vendor/github.com/gorilla/csrf/store.go @@ -0,0 +1,82 @@ +package csrf + +import ( + "net/http" + "time" + + "github.com/gorilla/securecookie" +) + +// store represents the session storage used for CSRF tokens. +type store interface { + // Get returns the real CSRF token from the store. + Get(*http.Request) ([]byte, error) + // Save stores the real CSRF token in the store and writes a + // cookie to the http.ResponseWriter. + // For non-cookie stores, the cookie should contain a unique (256 bit) ID + // or key that references the token in the backend store. + // csrf.GenerateRandomBytes is a helper function for generating secure IDs. + Save(token []byte, w http.ResponseWriter) error +} + +// cookieStore is a signed cookie session store for CSRF tokens. +type cookieStore struct { + name string + maxAge int + secure bool + httpOnly bool + path string + domain string + sc *securecookie.SecureCookie +} + +// Get retrieves a CSRF token from the session cookie. It returns an empty token +// if decoding fails (e.g. HMAC validation fails or the named cookie doesn't exist). +func (cs *cookieStore) Get(r *http.Request) ([]byte, error) { + // Retrieve the cookie from the request + cookie, err := r.Cookie(cs.name) + if err != nil { + return nil, err + } + + token := make([]byte, tokenLength) + // Decode the HMAC authenticated cookie. + err = cs.sc.Decode(cs.name, cookie.Value, &token) + if err != nil { + return nil, err + } + + return token, nil +} + +// Save stores the CSRF token in the session cookie. +func (cs *cookieStore) Save(token []byte, w http.ResponseWriter) error { + // Generate an encoded cookie value with the CSRF token. + encoded, err := cs.sc.Encode(cs.name, token) + if err != nil { + return err + } + + cookie := &http.Cookie{ + Name: cs.name, + Value: encoded, + MaxAge: cs.maxAge, + HttpOnly: cs.httpOnly, + Secure: cs.secure, + Path: cs.path, + Domain: cs.domain, + } + + // Set the Expires field on the cookie based on the MaxAge + // If MaxAge <= 0, we don't set the Expires attribute, making the cookie + // session-only. + if cs.maxAge > 0 { + cookie.Expires = time.Now().Add( + time.Duration(cs.maxAge) * time.Second) + } + + // Write the authenticated cookie to the response. + http.SetCookie(w, cookie) + + return nil +} |