From 44fef1c2a163bdfd781ef08a06e3cf5cf9b7d5da Mon Sep 17 00:00:00 2001 From: Niall Sheridan Date: Sun, 17 Jul 2016 23:54:42 +0100 Subject: 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 --- vendor/github.com/gorilla/csrf/csrf.go | 276 +++++++++++++++++++++++++++++++++ 1 file changed, 276 insertions(+) create mode 100644 vendor/github.com/gorilla/csrf/csrf.go (limited to 'vendor/github.com/gorilla/csrf/csrf.go') 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 +} -- cgit v1.2.3