Adding upstream version 2.52.6.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
a960158181
commit
6d002e9543
441 changed files with 95392 additions and 0 deletions
243
middleware/csrf/config.go
Normal file
243
middleware/csrf/config.go
Normal file
|
@ -0,0 +1,243 @@
|
|||
package csrf
|
||||
|
||||
import (
|
||||
"net/textproto"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/log"
|
||||
"github.com/gofiber/fiber/v2/middleware/session"
|
||||
"github.com/gofiber/fiber/v2/utils"
|
||||
)
|
||||
|
||||
// Config defines the config for middleware.
|
||||
type Config struct {
|
||||
// Next defines a function to skip this middleware when returned true.
|
||||
//
|
||||
// Optional. Default: nil
|
||||
Next func(c *fiber.Ctx) bool
|
||||
|
||||
// KeyLookup is a string in the form of "<source>:<key>" that is used
|
||||
// to create an Extractor that extracts the token from the request.
|
||||
// Possible values:
|
||||
// - "header:<name>"
|
||||
// - "query:<name>"
|
||||
// - "param:<name>"
|
||||
// - "form:<name>"
|
||||
// - "cookie:<name>"
|
||||
//
|
||||
// Ignored if an Extractor is explicitly set.
|
||||
//
|
||||
// Optional. Default: "header:X-Csrf-Token"
|
||||
KeyLookup string
|
||||
|
||||
// Name of the session cookie. This cookie will store session key.
|
||||
// Optional. Default value "csrf_".
|
||||
// Overridden if KeyLookup == "cookie:<name>"
|
||||
CookieName string
|
||||
|
||||
// Domain of the CSRF cookie.
|
||||
// Optional. Default value "".
|
||||
CookieDomain string
|
||||
|
||||
// Path of the CSRF cookie.
|
||||
// Optional. Default value "".
|
||||
CookiePath string
|
||||
|
||||
// Indicates if CSRF cookie is secure.
|
||||
// Optional. Default value false.
|
||||
CookieSecure bool
|
||||
|
||||
// Indicates if CSRF cookie is HTTP only.
|
||||
// Optional. Default value false.
|
||||
CookieHTTPOnly bool
|
||||
|
||||
// Value of SameSite cookie.
|
||||
// Optional. Default value "Lax".
|
||||
CookieSameSite string
|
||||
|
||||
// Decides whether cookie should last for only the browser sesison.
|
||||
// Ignores Expiration if set to true
|
||||
CookieSessionOnly bool
|
||||
|
||||
// Expiration is the duration before csrf token will expire
|
||||
//
|
||||
// Optional. Default: 1 * time.Hour
|
||||
Expiration time.Duration
|
||||
|
||||
// SingleUseToken indicates if the CSRF token be destroyed
|
||||
// and a new one generated on each use.
|
||||
//
|
||||
// Optional. Default: false
|
||||
SingleUseToken bool
|
||||
|
||||
// Store is used to store the state of the middleware
|
||||
//
|
||||
// Optional. Default: memory.New()
|
||||
// Ignored if Session is set.
|
||||
Storage fiber.Storage
|
||||
|
||||
// Session is used to store the state of the middleware
|
||||
//
|
||||
// Optional. Default: nil
|
||||
// If set, the middleware will use the session store instead of the storage
|
||||
Session *session.Store
|
||||
|
||||
// SessionKey is the key used to store the token in the session
|
||||
//
|
||||
// Default: "fiber.csrf.token"
|
||||
SessionKey string
|
||||
|
||||
// Context key to store generated CSRF token into context.
|
||||
// If left empty, token will not be stored in context.
|
||||
//
|
||||
// Optional. Default: ""
|
||||
ContextKey interface{}
|
||||
|
||||
// KeyGenerator creates a new CSRF token
|
||||
//
|
||||
// Optional. Default: utils.UUID
|
||||
KeyGenerator func() string
|
||||
|
||||
// Deprecated: Please use Expiration
|
||||
CookieExpires time.Duration
|
||||
|
||||
// Deprecated: Please use Cookie* related fields
|
||||
Cookie *fiber.Cookie
|
||||
|
||||
// Deprecated: Please use KeyLookup
|
||||
TokenLookup string
|
||||
|
||||
// ErrorHandler is executed when an error is returned from fiber.Handler.
|
||||
//
|
||||
// Optional. Default: DefaultErrorHandler
|
||||
ErrorHandler fiber.ErrorHandler
|
||||
|
||||
// Extractor returns the csrf token
|
||||
//
|
||||
// If set this will be used in place of an Extractor based on KeyLookup.
|
||||
//
|
||||
// Optional. Default will create an Extractor based on KeyLookup.
|
||||
Extractor func(c *fiber.Ctx) (string, error)
|
||||
|
||||
// HandlerContextKey is used to store the CSRF Handler into context
|
||||
//
|
||||
// Default: "fiber.csrf.handler"
|
||||
HandlerContextKey interface{}
|
||||
}
|
||||
|
||||
const HeaderName = "X-Csrf-Token"
|
||||
|
||||
// ConfigDefault is the default config
|
||||
var ConfigDefault = Config{
|
||||
KeyLookup: "header:" + HeaderName,
|
||||
CookieName: "csrf_",
|
||||
CookieSameSite: "Lax",
|
||||
Expiration: 1 * time.Hour,
|
||||
KeyGenerator: utils.UUIDv4,
|
||||
ErrorHandler: defaultErrorHandler,
|
||||
Extractor: CsrfFromHeader(HeaderName),
|
||||
SessionKey: "fiber.csrf.token",
|
||||
HandlerContextKey: "fiber.csrf.handler",
|
||||
}
|
||||
|
||||
// default ErrorHandler that process return error from fiber.Handler
|
||||
func defaultErrorHandler(_ *fiber.Ctx, _ error) error {
|
||||
return fiber.ErrForbidden
|
||||
}
|
||||
|
||||
// Helper function to set default values
|
||||
func configDefault(config ...Config) Config {
|
||||
// Return default config if nothing provided
|
||||
if len(config) < 1 {
|
||||
return ConfigDefault
|
||||
}
|
||||
|
||||
// Override default config
|
||||
cfg := config[0]
|
||||
|
||||
// Set default values
|
||||
if cfg.TokenLookup != "" {
|
||||
log.Warn("[CSRF] TokenLookup is deprecated, please use KeyLookup")
|
||||
cfg.KeyLookup = cfg.TokenLookup
|
||||
}
|
||||
if int(cfg.CookieExpires.Seconds()) > 0 {
|
||||
log.Warn("[CSRF] CookieExpires is deprecated, please use Expiration")
|
||||
cfg.Expiration = cfg.CookieExpires
|
||||
}
|
||||
if cfg.Cookie != nil {
|
||||
log.Warn("[CSRF] Cookie is deprecated, please use Cookie* related fields")
|
||||
if cfg.Cookie.Name != "" {
|
||||
cfg.CookieName = cfg.Cookie.Name
|
||||
}
|
||||
if cfg.Cookie.Domain != "" {
|
||||
cfg.CookieDomain = cfg.Cookie.Domain
|
||||
}
|
||||
if cfg.Cookie.Path != "" {
|
||||
cfg.CookiePath = cfg.Cookie.Path
|
||||
}
|
||||
cfg.CookieSecure = cfg.Cookie.Secure
|
||||
cfg.CookieHTTPOnly = cfg.Cookie.HTTPOnly
|
||||
if cfg.Cookie.SameSite != "" {
|
||||
cfg.CookieSameSite = cfg.Cookie.SameSite
|
||||
}
|
||||
}
|
||||
if cfg.KeyLookup == "" {
|
||||
cfg.KeyLookup = ConfigDefault.KeyLookup
|
||||
}
|
||||
if int(cfg.Expiration.Seconds()) <= 0 {
|
||||
cfg.Expiration = ConfigDefault.Expiration
|
||||
}
|
||||
if cfg.CookieName == "" {
|
||||
cfg.CookieName = ConfigDefault.CookieName
|
||||
}
|
||||
if cfg.CookieSameSite == "" {
|
||||
cfg.CookieSameSite = ConfigDefault.CookieSameSite
|
||||
}
|
||||
if cfg.KeyGenerator == nil {
|
||||
cfg.KeyGenerator = ConfigDefault.KeyGenerator
|
||||
}
|
||||
if cfg.ErrorHandler == nil {
|
||||
cfg.ErrorHandler = ConfigDefault.ErrorHandler
|
||||
}
|
||||
if cfg.SessionKey == "" {
|
||||
cfg.SessionKey = ConfigDefault.SessionKey
|
||||
}
|
||||
if cfg.HandlerContextKey == nil {
|
||||
cfg.HandlerContextKey = ConfigDefault.HandlerContextKey
|
||||
}
|
||||
|
||||
// Generate the correct extractor to get the token from the correct location
|
||||
selectors := strings.Split(cfg.KeyLookup, ":")
|
||||
|
||||
const numParts = 2
|
||||
if len(selectors) != numParts {
|
||||
panic("[CSRF] KeyLookup must in the form of <source>:<key>")
|
||||
}
|
||||
|
||||
if cfg.Extractor == nil {
|
||||
// By default we extract from a header
|
||||
cfg.Extractor = CsrfFromHeader(textproto.CanonicalMIMEHeaderKey(selectors[1]))
|
||||
|
||||
switch selectors[0] {
|
||||
case "form":
|
||||
cfg.Extractor = CsrfFromForm(selectors[1])
|
||||
case "query":
|
||||
cfg.Extractor = CsrfFromQuery(selectors[1])
|
||||
case "param":
|
||||
cfg.Extractor = CsrfFromParam(selectors[1])
|
||||
case "cookie":
|
||||
if cfg.Session == nil {
|
||||
log.Warn("[CSRF] Cookie extractor is not recommended without a session store")
|
||||
}
|
||||
if cfg.CookieSameSite == "None" || cfg.CookieSameSite != "Lax" && cfg.CookieSameSite != "Strict" {
|
||||
log.Warn("[CSRF] Cookie extractor is only recommended for use with SameSite=Lax or SameSite=Strict")
|
||||
}
|
||||
cfg.Extractor = CsrfFromCookie(selectors[1])
|
||||
cfg.CookieName = selectors[1] // Cookie name is the same as the key
|
||||
}
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
239
middleware/csrf/csrf.go
Normal file
239
middleware/csrf/csrf.go
Normal file
|
@ -0,0 +1,239 @@
|
|||
package csrf
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrTokenNotFound = errors.New("csrf token not found")
|
||||
ErrTokenInvalid = errors.New("csrf token invalid")
|
||||
ErrNoReferer = errors.New("referer not supplied")
|
||||
ErrBadReferer = errors.New("referer invalid")
|
||||
dummyValue = []byte{'+'}
|
||||
)
|
||||
|
||||
type CSRFHandler struct {
|
||||
config *Config
|
||||
sessionManager *sessionManager
|
||||
storageManager *storageManager
|
||||
}
|
||||
|
||||
// New creates a new middleware handler
|
||||
func New(config ...Config) fiber.Handler {
|
||||
// Set default config
|
||||
cfg := configDefault(config...)
|
||||
|
||||
// Create manager to simplify storage operations ( see *_manager.go )
|
||||
var sessionManager *sessionManager
|
||||
var storageManager *storageManager
|
||||
if cfg.Session != nil {
|
||||
// Register the Token struct in the session store
|
||||
cfg.Session.RegisterType(Token{})
|
||||
|
||||
sessionManager = newSessionManager(cfg.Session, cfg.SessionKey)
|
||||
} else {
|
||||
storageManager = newStorageManager(cfg.Storage)
|
||||
}
|
||||
|
||||
// Return new handler
|
||||
return func(c *fiber.Ctx) error {
|
||||
// Don't execute middleware if Next returns true
|
||||
if cfg.Next != nil && cfg.Next(c) {
|
||||
return c.Next()
|
||||
}
|
||||
|
||||
// Store the CSRF handler in the context if a context key is specified
|
||||
if cfg.HandlerContextKey != "" {
|
||||
c.Locals(cfg.HandlerContextKey, &CSRFHandler{
|
||||
config: &cfg,
|
||||
sessionManager: sessionManager,
|
||||
storageManager: storageManager,
|
||||
})
|
||||
}
|
||||
|
||||
var token string
|
||||
|
||||
// Action depends on the HTTP method
|
||||
switch c.Method() {
|
||||
case fiber.MethodGet, fiber.MethodHead, fiber.MethodOptions, fiber.MethodTrace:
|
||||
cookieToken := c.Cookies(cfg.CookieName)
|
||||
|
||||
if cookieToken != "" {
|
||||
raw := getRawFromStorage(c, cookieToken, cfg, sessionManager, storageManager)
|
||||
|
||||
if raw != nil {
|
||||
token = cookieToken // Token is valid, safe to set it
|
||||
}
|
||||
}
|
||||
default:
|
||||
// Assume that anything not defined as 'safe' by RFC7231 needs protection
|
||||
|
||||
// Enforce an origin check for HTTPS connections.
|
||||
if c.Protocol() == "https" {
|
||||
if err := refererMatchesHost(c); err != nil {
|
||||
return cfg.ErrorHandler(c, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Extract token from client request i.e. header, query, param, form or cookie
|
||||
extractedToken, err := cfg.Extractor(c)
|
||||
if err != nil {
|
||||
return cfg.ErrorHandler(c, err)
|
||||
}
|
||||
|
||||
if extractedToken == "" {
|
||||
return cfg.ErrorHandler(c, ErrTokenNotFound)
|
||||
}
|
||||
|
||||
// If not using CsrfFromCookie extractor, check that the token matches the cookie
|
||||
// This is to prevent CSRF attacks by using a Double Submit Cookie method
|
||||
// Useful when we do not have access to the users Session
|
||||
if !isCsrfFromCookie(cfg.Extractor) && !compareStrings(extractedToken, c.Cookies(cfg.CookieName)) {
|
||||
return cfg.ErrorHandler(c, ErrTokenInvalid)
|
||||
}
|
||||
|
||||
raw := getRawFromStorage(c, extractedToken, cfg, sessionManager, storageManager)
|
||||
|
||||
if raw == nil {
|
||||
// If token is not in storage, expire the cookie
|
||||
expireCSRFCookie(c, cfg)
|
||||
// and return an error
|
||||
return cfg.ErrorHandler(c, ErrTokenNotFound)
|
||||
}
|
||||
if cfg.SingleUseToken {
|
||||
// If token is single use, delete it from storage
|
||||
deleteTokenFromStorage(c, extractedToken, cfg, sessionManager, storageManager)
|
||||
} else {
|
||||
token = extractedToken // Token is valid, safe to set it
|
||||
}
|
||||
}
|
||||
|
||||
// Generate CSRF token if not exist
|
||||
if token == "" {
|
||||
// And generate a new token
|
||||
token = cfg.KeyGenerator()
|
||||
}
|
||||
|
||||
// Create or extend the token in the storage
|
||||
createOrExtendTokenInStorage(c, token, cfg, sessionManager, storageManager)
|
||||
|
||||
// Update the CSRF cookie
|
||||
updateCSRFCookie(c, cfg, token)
|
||||
|
||||
// Tell the browser that a new header value is generated
|
||||
c.Vary(fiber.HeaderCookie)
|
||||
|
||||
// Store the token in the context if a context key is specified
|
||||
if cfg.ContextKey != nil {
|
||||
c.Locals(cfg.ContextKey, token)
|
||||
}
|
||||
|
||||
// Continue stack
|
||||
return c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// getRawFromStorage returns the raw value from the storage for the given token
|
||||
// returns nil if the token does not exist, is expired or is invalid
|
||||
func getRawFromStorage(c *fiber.Ctx, token string, cfg Config, sessionManager *sessionManager, storageManager *storageManager) []byte {
|
||||
if cfg.Session != nil {
|
||||
return sessionManager.getRaw(c, token, dummyValue)
|
||||
}
|
||||
return storageManager.getRaw(token)
|
||||
}
|
||||
|
||||
// createOrExtendTokenInStorage creates or extends the token in the storage
|
||||
func createOrExtendTokenInStorage(c *fiber.Ctx, token string, cfg Config, sessionManager *sessionManager, storageManager *storageManager) {
|
||||
if cfg.Session != nil {
|
||||
sessionManager.setRaw(c, token, dummyValue, cfg.Expiration)
|
||||
} else {
|
||||
storageManager.setRaw(token, dummyValue, cfg.Expiration)
|
||||
}
|
||||
}
|
||||
|
||||
func deleteTokenFromStorage(c *fiber.Ctx, token string, cfg Config, sessionManager *sessionManager, storageManager *storageManager) {
|
||||
if cfg.Session != nil {
|
||||
sessionManager.delRaw(c)
|
||||
} else {
|
||||
storageManager.delRaw(token)
|
||||
}
|
||||
}
|
||||
|
||||
// Update CSRF cookie
|
||||
// if expireCookie is true, the cookie will expire immediately
|
||||
func updateCSRFCookie(c *fiber.Ctx, cfg Config, token string) {
|
||||
setCSRFCookie(c, cfg, token, cfg.Expiration)
|
||||
}
|
||||
|
||||
func expireCSRFCookie(c *fiber.Ctx, cfg Config) {
|
||||
setCSRFCookie(c, cfg, "", -time.Hour)
|
||||
}
|
||||
|
||||
func setCSRFCookie(c *fiber.Ctx, cfg Config, token string, expiry time.Duration) {
|
||||
cookie := &fiber.Cookie{
|
||||
Name: cfg.CookieName,
|
||||
Value: token,
|
||||
Domain: cfg.CookieDomain,
|
||||
Path: cfg.CookiePath,
|
||||
Secure: cfg.CookieSecure,
|
||||
HTTPOnly: cfg.CookieHTTPOnly,
|
||||
SameSite: cfg.CookieSameSite,
|
||||
SessionOnly: cfg.CookieSessionOnly,
|
||||
Expires: time.Now().Add(expiry),
|
||||
}
|
||||
|
||||
// Set the CSRF cookie to the response
|
||||
c.Cookie(cookie)
|
||||
}
|
||||
|
||||
// DeleteToken removes the token found in the context from the storage
|
||||
// and expires the CSRF cookie
|
||||
func (handler *CSRFHandler) DeleteToken(c *fiber.Ctx) error {
|
||||
// Get the config from the context
|
||||
config := handler.config
|
||||
if config == nil {
|
||||
panic("CSRFHandler config not found in context")
|
||||
}
|
||||
// Extract token from the client request cookie
|
||||
cookieToken := c.Cookies(config.CookieName)
|
||||
if cookieToken == "" {
|
||||
return config.ErrorHandler(c, ErrTokenNotFound)
|
||||
}
|
||||
// Remove the token from storage
|
||||
deleteTokenFromStorage(c, cookieToken, *config, handler.sessionManager, handler.storageManager)
|
||||
// Expire the cookie
|
||||
expireCSRFCookie(c, *config)
|
||||
return nil
|
||||
}
|
||||
|
||||
// isCsrfFromCookie checks if the extractor is set to ExtractFromCookie
|
||||
func isCsrfFromCookie(extractor interface{}) bool {
|
||||
return reflect.ValueOf(extractor).Pointer() == reflect.ValueOf(CsrfFromCookie).Pointer()
|
||||
}
|
||||
|
||||
// refererMatchesHost checks that the referer header matches the host header
|
||||
// returns an error if the referer header is not present or is invalid
|
||||
// returns nil if the referer header is valid
|
||||
func refererMatchesHost(c *fiber.Ctx) error {
|
||||
referer := strings.ToLower(c.Get(fiber.HeaderReferer))
|
||||
if referer == "" {
|
||||
return ErrNoReferer
|
||||
}
|
||||
|
||||
refererURL, err := url.Parse(referer)
|
||||
if err != nil {
|
||||
return ErrBadReferer
|
||||
}
|
||||
|
||||
if refererURL.Scheme == c.Protocol() && refererURL.Host == c.Hostname() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ErrBadReferer
|
||||
}
|
1060
middleware/csrf/csrf_test.go
Normal file
1060
middleware/csrf/csrf_test.go
Normal file
File diff suppressed because it is too large
Load diff
70
middleware/csrf/extractors.go
Normal file
70
middleware/csrf/extractors.go
Normal file
|
@ -0,0 +1,70 @@
|
|||
package csrf
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrMissingHeader = errors.New("missing csrf token in header")
|
||||
ErrMissingQuery = errors.New("missing csrf token in query")
|
||||
ErrMissingParam = errors.New("missing csrf token in param")
|
||||
ErrMissingForm = errors.New("missing csrf token in form")
|
||||
ErrMissingCookie = errors.New("missing csrf token in cookie")
|
||||
)
|
||||
|
||||
// csrfFromParam returns a function that extracts token from the url param string.
|
||||
func CsrfFromParam(param string) func(c *fiber.Ctx) (string, error) {
|
||||
return func(c *fiber.Ctx) (string, error) {
|
||||
token := c.Params(param)
|
||||
if token == "" {
|
||||
return "", ErrMissingParam
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
}
|
||||
|
||||
// csrfFromForm returns a function that extracts a token from a multipart-form.
|
||||
func CsrfFromForm(param string) func(c *fiber.Ctx) (string, error) {
|
||||
return func(c *fiber.Ctx) (string, error) {
|
||||
token := c.FormValue(param)
|
||||
if token == "" {
|
||||
return "", ErrMissingForm
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
}
|
||||
|
||||
// csrfFromCookie returns a function that extracts token from the cookie header.
|
||||
func CsrfFromCookie(param string) func(c *fiber.Ctx) (string, error) {
|
||||
return func(c *fiber.Ctx) (string, error) {
|
||||
token := c.Cookies(param)
|
||||
if token == "" {
|
||||
return "", ErrMissingCookie
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
}
|
||||
|
||||
// csrfFromHeader returns a function that extracts token from the request header.
|
||||
func CsrfFromHeader(param string) func(c *fiber.Ctx) (string, error) {
|
||||
return func(c *fiber.Ctx) (string, error) {
|
||||
token := c.Get(param)
|
||||
if token == "" {
|
||||
return "", ErrMissingHeader
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
}
|
||||
|
||||
// csrfFromQuery returns a function that extracts token from the query string.
|
||||
func CsrfFromQuery(param string) func(c *fiber.Ctx) (string, error) {
|
||||
return func(c *fiber.Ctx) (string, error) {
|
||||
token := c.Query(param)
|
||||
if token == "" {
|
||||
return "", ErrMissingQuery
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
}
|
13
middleware/csrf/helpers.go
Normal file
13
middleware/csrf/helpers.go
Normal file
|
@ -0,0 +1,13 @@
|
|||
package csrf
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
)
|
||||
|
||||
func compareTokens(a, b []byte) bool {
|
||||
return subtle.ConstantTimeCompare(a, b) == 1
|
||||
}
|
||||
|
||||
func compareStrings(a, b string) bool {
|
||||
return subtle.ConstantTimeCompare([]byte(a), []byte(b)) == 1
|
||||
}
|
68
middleware/csrf/session_manager.go
Normal file
68
middleware/csrf/session_manager.go
Normal file
|
@ -0,0 +1,68 @@
|
|||
package csrf
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/log"
|
||||
"github.com/gofiber/fiber/v2/middleware/session"
|
||||
)
|
||||
|
||||
type sessionManager struct {
|
||||
key string
|
||||
session *session.Store
|
||||
}
|
||||
|
||||
func newSessionManager(s *session.Store, k string) *sessionManager {
|
||||
// Create new storage handler
|
||||
sessionManager := &sessionManager{
|
||||
key: k,
|
||||
}
|
||||
if s != nil {
|
||||
// Use provided storage if provided
|
||||
sessionManager.session = s
|
||||
}
|
||||
return sessionManager
|
||||
}
|
||||
|
||||
// get token from session
|
||||
func (m *sessionManager) getRaw(c *fiber.Ctx, key string, raw []byte) []byte {
|
||||
sess, err := m.session.Get(c)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
token, ok := sess.Get(m.key).(Token)
|
||||
if ok {
|
||||
if token.Expiration.Before(time.Now()) || key != token.Key || !compareTokens(raw, token.Raw) {
|
||||
return nil
|
||||
}
|
||||
return token.Raw
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// set token in session
|
||||
func (m *sessionManager) setRaw(c *fiber.Ctx, key string, raw []byte, exp time.Duration) {
|
||||
sess, err := m.session.Get(c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// the key is crucial in crsf and sometimes a reference to another value which can be reused later(pool/unsafe values concept), so a copy is made here
|
||||
sess.Set(m.key, &Token{key, raw, time.Now().Add(exp)})
|
||||
if err := sess.Save(); err != nil {
|
||||
log.Warn("csrf: failed to save session: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
// delete token from session
|
||||
func (m *sessionManager) delRaw(c *fiber.Ctx) {
|
||||
sess, err := m.session.Get(c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
sess.Delete(m.key)
|
||||
if err := sess.Save(); err != nil {
|
||||
log.Warn("csrf: failed to save session: ", err)
|
||||
}
|
||||
}
|
70
middleware/csrf/storage_manager.go
Normal file
70
middleware/csrf/storage_manager.go
Normal file
|
@ -0,0 +1,70 @@
|
|||
package csrf
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/internal/memory"
|
||||
"github.com/gofiber/fiber/v2/utils"
|
||||
)
|
||||
|
||||
// go:generate msgp
|
||||
// msgp -file="storage_manager.go" -o="storage_manager_msgp.go" -tests=false -unexported
|
||||
type item struct{}
|
||||
|
||||
//msgp:ignore manager
|
||||
type storageManager struct {
|
||||
pool sync.Pool
|
||||
memory *memory.Storage
|
||||
storage fiber.Storage
|
||||
}
|
||||
|
||||
func newStorageManager(storage fiber.Storage) *storageManager {
|
||||
// Create new storage handler
|
||||
storageManager := &storageManager{
|
||||
pool: sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(item)
|
||||
},
|
||||
},
|
||||
}
|
||||
if storage != nil {
|
||||
// Use provided storage if provided
|
||||
storageManager.storage = storage
|
||||
} else {
|
||||
// Fallback too memory storage
|
||||
storageManager.memory = memory.New()
|
||||
}
|
||||
return storageManager
|
||||
}
|
||||
|
||||
// get raw data from storage or memory
|
||||
func (m *storageManager) getRaw(key string) []byte {
|
||||
var raw []byte
|
||||
if m.storage != nil {
|
||||
raw, _ = m.storage.Get(key) //nolint:errcheck // TODO: Do not ignore error
|
||||
} else {
|
||||
raw, _ = m.memory.Get(key).([]byte) //nolint:errcheck // TODO: Do not ignore error
|
||||
}
|
||||
return raw
|
||||
}
|
||||
|
||||
// set data to storage or memory
|
||||
func (m *storageManager) setRaw(key string, raw []byte, exp time.Duration) {
|
||||
if m.storage != nil {
|
||||
_ = m.storage.Set(key, raw, exp) //nolint:errcheck // TODO: Do not ignore error
|
||||
} else {
|
||||
// the key is crucial in crsf and sometimes a reference to another value which can be reused later(pool/unsafe values concept), so a copy is made here
|
||||
m.memory.Set(utils.CopyString(key), raw, exp)
|
||||
}
|
||||
}
|
||||
|
||||
// delete data from storage or memory
|
||||
func (m *storageManager) delRaw(key string) {
|
||||
if m.storage != nil {
|
||||
_ = m.storage.Delete(key) //nolint:errcheck // TODO: Do not ignore error
|
||||
} else {
|
||||
m.memory.Delete(key)
|
||||
}
|
||||
}
|
90
middleware/csrf/storage_manager_msgp.go
Normal file
90
middleware/csrf/storage_manager_msgp.go
Normal file
|
@ -0,0 +1,90 @@
|
|||
package csrf
|
||||
|
||||
// Code generated by github.com/tinylib/msgp DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"github.com/tinylib/msgp/msgp"
|
||||
)
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *item) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, err = dc.ReadMapKeyPtr()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z item) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// map header, size 0
|
||||
err = en.Append(0x80)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z item) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// map header, size 0
|
||||
o = append(o, 0x80)
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *item) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, bts, err = msgp.ReadMapKeyZC(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z item) Msgsize() (s int) {
|
||||
s = 1
|
||||
return
|
||||
}
|
11
middleware/csrf/token.go
Normal file
11
middleware/csrf/token.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
package csrf
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type Token struct {
|
||||
Key string `json:"key"`
|
||||
Raw []byte `json:"raw"`
|
||||
Expiration time.Time `json:"expiration"`
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue