// CSRFWithConfig returns a CSRF middleware from config. // See `CSRF()`. func CSRFWithConfig(config CSRFConfig) echo.MiddlewareFunc { // Defaults if config.Secret == nil { panic("csrf secret must be provided") } if config.TokenLookup == "" { config.TokenLookup = DefaultCSRFConfig.TokenLookup } if config.ContextKey == "" { config.ContextKey = DefaultCSRFConfig.ContextKey } if config.CookieName == "" { config.CookieName = DefaultCSRFConfig.CookieName } if config.CookieExpires.IsZero() { config.CookieExpires = DefaultCSRFConfig.CookieExpires } // Initialize parts := strings.Split(config.TokenLookup, ":") extractor := csrfTokenFromHeader(parts[1]) switch parts[0] { case "form": extractor = csrfTokenFromForm(parts[1]) case "query": extractor = csrfTokenFromQuery(parts[1]) } return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { req := c.Request() // Set CSRF token salt, err := generateSalt(8) if err != nil { return err } token := generateCSRFToken(config.Secret, salt) c.Set(config.ContextKey, token) cookie := new(echo.Cookie) cookie.SetName(config.CookieName) cookie.SetValue(token) if config.CookiePath != "" { cookie.SetPath(config.CookiePath) } if config.CookieDomain != "" { cookie.SetDomain(config.CookieDomain) } cookie.SetExpires(config.CookieExpires) cookie.SetSecure(config.CookieSecure) cookie.SetHTTPOnly(config.CookieHTTPOnly) c.SetCookie(cookie) switch req.Method() { case echo.GET, echo.HEAD, echo.OPTIONS, echo.TRACE: default: token, err := extractor(c) if err != nil { return err } ok, err := validateCSRFToken(token, config.Secret) if err != nil { return err } if !ok { return echo.NewHTTPError(http.StatusForbidden, "invalid csrf token") } } return next(c) } } }
// CSRFWithConfig returns a CSRF middleware from config. // See `CSRF()`. func CSRFWithConfig(config CSRFConfig) echo.MiddlewareFunc { // Defaults if config.Skipper == nil { config.Skipper = DefaultCSRFConfig.Skipper } if config.TokenLength == 0 { config.TokenLength = DefaultCSRFConfig.TokenLength } if config.TokenLookup == "" { config.TokenLookup = DefaultCSRFConfig.TokenLookup } if config.ContextKey == "" { config.ContextKey = DefaultCSRFConfig.ContextKey } if config.CookieName == "" { config.CookieName = DefaultCSRFConfig.CookieName } if config.CookieMaxAge == 0 { config.CookieMaxAge = DefaultCSRFConfig.CookieMaxAge } // Initialize parts := strings.Split(config.TokenLookup, ":") extractor := csrfTokenFromHeader(parts[1]) switch parts[0] { case "form": extractor = csrfTokenFromForm(parts[1]) case "query": extractor = csrfTokenFromQuery(parts[1]) } return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { if config.Skipper(c) { return next(c) } req := c.Request() k, err := c.Cookie(config.CookieName) token := "" if err != nil { // Generate token token = random.String(config.TokenLength) } else { // Reuse token token = k.Value() } switch req.Method() { case echo.GET, echo.HEAD, echo.OPTIONS, echo.TRACE: default: // Validate token only for requests which are not defined as 'safe' by RFC7231 clientToken, err := extractor(c) if err != nil { return err } if !validateCSRFToken(token, clientToken) { return echo.NewHTTPError(http.StatusForbidden, "csrf token is invalid") } } // Set CSRF cookie cookie := new(echo.Cookie) cookie.SetName(config.CookieName) cookie.SetValue(token) if config.CookiePath != "" { cookie.SetPath(config.CookiePath) } if config.CookieDomain != "" { cookie.SetDomain(config.CookieDomain) } cookie.SetExpires(time.Now().Add(time.Duration(config.CookieMaxAge) * time.Second)) cookie.SetSecure(config.CookieSecure) cookie.SetHTTPOnly(config.CookieHTTPOnly) c.SetCookie(cookie) // Store token in the context c.Set(config.ContextKey, token) // Protect clients from caching the response c.Response().Header().Add(echo.HeaderVary, echo.HeaderCookie) return next(c) } } }