csrf

package
v1.4.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 3, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Overview

Package csrf provides Cross-Site Request Forgery protection middleware for celeris using the double-submit cookie pattern, with optional server-side token storage for enhanced security.

On safe HTTP methods (GET, HEAD, OPTIONS, TRACE by default) the middleware generates or reuses a CSRF token, sets it as a cookie, and stores it in the request context. On unsafe methods (POST, PUT, DELETE, PATCH) it performs defense-in-depth checks (Sec-Fetch-Site, Origin, Referer) then compares the cookie token against the request token using constant-time comparison.

Default usage (token in X-CSRF-Token header):

server.Use(csrf.New())

Custom token lookup from a form field:

server.Use(csrf.New(csrf.Config{
    TokenLookup: "form:_csrf",
}))

Retrieving the Token

Use TokenFromContext to read the token in downstream handlers:

token := csrf.TokenFromContext(c)

Server-Side Token Storage

For enhanced security, configure Config.Storage to validate tokens against a server-side store (signed double-submit):

store := csrf.NewMemoryStorage()
server.Use(csrf.New(csrf.Config{
    Storage:    store,
    Expiration: 2 * time.Hour,
}))

Session Cookies

Set Config.CookieMaxAge to 0 for a browser-session-scoped cookie:

server.Use(csrf.New(csrf.Config{
    CookieMaxAge: 0,
}))

Trusted Origins

Config.TrustedOrigins allows cross-origin requests from specific origins. Wildcard subdomain patterns are supported:

server.Use(csrf.New(csrf.Config{
    TrustedOrigins: []string{
        "https://app.example.com",
        "https://*.example.com",
    },
}))

Sentinel Errors

The package exports sentinel errors (ErrForbidden, ErrMissingToken, ErrTokenNotFound, ErrOriginMismatch, ErrRefererMissing, ErrRefererMismatch, ErrSecFetchSite) for use with errors.Is.

Security

CookieSecure defaults to false for development convenience. Production deployments MUST set CookieSecure: true to prevent cookie transmission over unencrypted connections.

CookieHTTPOnly is always enforced as true regardless of the user-supplied Config value. This prevents client-side JavaScript from reading the CSRF cookie, which is a defense-in-depth measure against XSS token theft.

Method Override Interaction

When using the methodoverride middleware (registered via Server.Pre()), the HTTP method is rewritten before CSRF validation. A POST request with X-HTTP-Method-Override: PUT becomes a PUT by the time CSRF runs.

IMPORTANT: Do not add PUT, DELETE, or PATCH to SafeMethods if you use method override. Doing so would allow form submissions to bypass CSRF token validation by tunneling through POST → PUT/DELETE/PATCH.

The default SafeMethods (GET, HEAD, OPTIONS, TRACE) are safe because methodoverride only overrides POST requests, and the typical override targets (PUT, DELETE, PATCH) are not in the CSRF safe set.

Index

Examples

Constants

View Source
const ContextKey = "csrf_token"

ContextKey is the default context store key for the CSRF token.

Variables

View Source
var (
	// ErrForbidden is returned when the submitted CSRF token does not match
	// the cookie token.
	ErrForbidden = celeris.NewHTTPError(403, "Forbidden")

	// ErrMissingToken is returned when the CSRF token is absent from the
	// cookie or the request source.
	ErrMissingToken = &celeris.HTTPError{Code: 403, Message: "Forbidden", Err: errors.New("csrf: missing token")}

	// ErrTokenNotFound is returned by DeleteToken when no token exists.
	ErrTokenNotFound = &celeris.HTTPError{Code: 404, Message: "Not Found", Err: errors.New("csrf: token not found")}

	// ErrOriginMismatch is returned when the Origin header does not match
	// the request host or any trusted origin.
	ErrOriginMismatch = &celeris.HTTPError{Code: 403, Message: "Forbidden", Err: errors.New("csrf: origin does not match")}

	// ErrRefererMissing is returned when the Referer header is absent on an
	// HTTPS request with no Origin header.
	ErrRefererMissing = &celeris.HTTPError{Code: 403, Message: "Forbidden", Err: errors.New("csrf: referer header missing")}

	// ErrRefererMismatch is returned when the Referer header does not match
	// the request host or any trusted origin.
	ErrRefererMismatch = &celeris.HTTPError{Code: 403, Message: "Forbidden", Err: errors.New("csrf: referer does not match")}

	// ErrSecFetchSite is returned when the Sec-Fetch-Site header value is
	// "cross-site", indicating a cross-site request.
	ErrSecFetchSite = &celeris.HTTPError{Code: 403, Message: "Forbidden", Err: errors.New("csrf: sec-fetch-site is cross-site")}
)

Sentinel errors returned by the CSRF middleware. These are package-level variables for use with errors.Is. Do not reassign them.

Functions

func DeleteToken

func DeleteToken(c *celeris.Context) error

DeleteToken removes the CSRF token from server-side storage and expires the cookie. This is a convenience wrapper around HandlerFromContext. Returns ErrTokenNotFound if no token or handler exists.

Example
package main

import (
	"github.com/goceleris/celeris"

	"github.com/goceleris/celeris/middleware/csrf"
)

func main() {
	// Delete token during logout.
	_ = func(c *celeris.Context) error {
		_ = csrf.DeleteToken(c)
		return c.String(200, "logged out")
	}
}

func New

func New(config ...Config) celeris.HandlerFunc

New creates a CSRF middleware with the given config.

Example
package main

import (
	"github.com/goceleris/celeris/middleware/csrf"
)

func main() {
	// Default: token extracted from X-CSRF-Token header.
	_ = csrf.New()
}
Example (CustomContextKey)
package main

import (
	"github.com/goceleris/celeris/middleware/csrf"
)

func main() {
	// Store token under a custom context key.
	_ = csrf.New(csrf.Config{
		ContextKey: "my_csrf_token",
	})
}
Example (CustomErrorHandler)
package main

import (
	"github.com/goceleris/celeris"

	"github.com/goceleris/celeris/middleware/csrf"
)

func main() {
	// Custom error handling for CSRF failures.
	_ = csrf.New(csrf.Config{
		ErrorHandler: func(_ *celeris.Context, _ error) error {
			return celeris.NewHTTPError(403, "CSRF validation failed")
		},
	})
}
Example (FormLookup)
package main

import (
	"github.com/goceleris/celeris/middleware/csrf"
)

func main() {
	// Extract token from a form field instead of a header.
	_ = csrf.New(csrf.Config{
		TokenLookup: "form:_csrf",
	})
}
Example (MultipleExtractors)
package main

import (
	"github.com/goceleris/celeris/middleware/csrf"
)

func main() {
	// Try header first, then fall back to form field.
	_ = csrf.New(csrf.Config{
		TokenLookup: "header:X-CSRF-Token,form:_csrf",
	})
}
Example (ServerSideStorage)
package main

import (
	"time"

	"github.com/goceleris/celeris/middleware/csrf"
)

func main() {
	// Enable server-side token storage for enhanced security.
	store := csrf.NewMemoryStorage()
	_ = csrf.New(csrf.Config{
		Storage:    store,
		Expiration: 2 * time.Hour,
	})
}
Example (SessionCookie)
package main

import (
	"github.com/goceleris/celeris/middleware/csrf"
)

func main() {
	// Browser-session-scoped cookie (MaxAge 0).
	_ = csrf.New(csrf.Config{
		CookieMaxAge: 0,
	})
}
Example (SingleUseToken)
package main

import (
	"github.com/goceleris/celeris/middleware/csrf"
)

func main() {
	// Single-use tokens: each token is consumed after validation.
	_ = csrf.New(csrf.Config{
		Storage:        csrf.NewMemoryStorage(),
		SingleUseToken: true,
	})
}
Example (WildcardTrustedOrigins)
package main

import (
	"github.com/goceleris/celeris/middleware/csrf"
)

func main() {
	// Allow all subdomains of example.com.
	_ = csrf.New(csrf.Config{
		TrustedOrigins: []string{"https://*.example.com"},
	})
}

func NewMemoryStorage

func NewMemoryStorage(config ...MemoryStorageConfig) *store.MemoryKV

NewMemoryStorage returns an in-memory Storage backed by store.MemoryKV. The returned *store.MemoryKV implements all optional store extensions; callers needing atomic GetAndDelete for SingleUseToken can rely on the built-in GetAndDeleter implementation.

func TokenFromContext

func TokenFromContext(c *celeris.Context) string

TokenFromContext returns the CSRF token from the context store. Returns an empty string if no token was stored (e.g., skipped request). When the middleware is configured with a custom ContextKey, the handler's key is tried first, falling back to the default ContextKey constant.

Example
package main

import (
	"github.com/goceleris/celeris"

	"github.com/goceleris/celeris/middleware/csrf"
)

func main() {
	// Retrieve the CSRF token inside a handler to pass to templates.
	_ = func(c *celeris.Context) error {
		token := csrf.TokenFromContext(c)
		return c.String(200, "token: %s", token)
	}
}

Types

type Config

type Config struct {
	// Skip defines a function to skip this middleware for certain requests.
	Skip func(c *celeris.Context) bool

	// SkipPaths lists paths to skip (exact match).
	SkipPaths []string

	// TokenLength is the number of random bytes used to generate the token.
	// The resulting hex-encoded token string is twice this length.
	// Default: 32.
	TokenLength int

	// TokenLookup defines the source(s) for extracting the CSRF token on
	// unsafe methods. Format is "source:name" where source is "header",
	// "form", or "query". Multiple sources can be comma-separated; the
	// first non-empty match is used. Default: "header:X-CSRF-Token".
	TokenLookup string

	// CookieName is the name of the CSRF cookie. Default: "_csrf".
	CookieName string

	// CookiePath is the path attribute of the CSRF cookie. Default: "/".
	CookiePath string

	// CookieDomain is the domain attribute of the CSRF cookie.
	CookieDomain string

	// CookieMaxAge is the max-age of the CSRF cookie in seconds.
	// Default: 86400 (24 hours). Set to 0 for a session cookie
	// (deleted when the browser closes).
	CookieMaxAge int

	// CookieSecure flags the cookie for HTTPS-only transmission.
	CookieSecure bool

	// CookieHTTPOnly controls the HttpOnly flag on the CSRF cookie.
	// This is always enforced as true for security — CSRF cookies must not
	// be readable by JavaScript. The field exists for documentation purposes
	// but is overridden by applyDefaults.
	CookieHTTPOnly bool

	// CookieSameSite controls the SameSite attribute of the CSRF cookie.
	// Default: celeris.SameSiteLaxMode.
	CookieSameSite celeris.SameSite

	// ErrorHandler handles CSRF validation failures. When nil, the
	// middleware returns the error directly (ErrMissingToken or ErrForbidden).
	ErrorHandler func(c *celeris.Context, err error) error

	// SafeMethods lists HTTP methods that do not require token validation.
	// Default: ["GET", "HEAD", "OPTIONS", "TRACE"].
	SafeMethods []string

	// TrustedOrigins lists additional origins that are allowed for
	// cross-origin requests. Each entry should be a full origin
	// (e.g. "https://app.example.com") or a wildcard subdomain pattern
	// (e.g. "https://*.example.com"). When the Origin header is present
	// on unsafe methods, it must match the request Host or one of these
	// entries. An empty list means only same-origin requests are allowed.
	TrustedOrigins []string

	// Storage enables server-side token storage (signed double-submit).
	// When set, tokens are stored in the backend and validated against
	// the store on unsafe methods. When nil, pure double-submit cookie
	// mode is used.
	//
	// For single-use tokens (SingleUseToken=true), backends that
	// implement [store.GetAndDeleter] (Redis 6.2+, MemoryKV) provide
	// TOCTOU-safe atomic fetch-and-delete; otherwise a non-atomic
	// Get+Delete pair is used.
	Storage store.KV

	// Expiration is the token lifetime in server-side storage.
	// Only used when Storage is set. Default: 1 hour.
	Expiration time.Duration

	// SingleUseToken, when true and Storage is set, deletes the token
	// from storage after successful validation. A new token is generated
	// on the next safe-method request.
	SingleUseToken bool

	// KeyGenerator provides a custom token generation function.
	// When nil, the default buffered hex generator is used.
	KeyGenerator func() string

	// ContextKey is the key used to store the CSRF token in the
	// request context. Default: "csrf_token".
	ContextKey string
}

Config defines the CSRF middleware configuration.

type Handler

type Handler struct {
	// contains filtered or unexported fields
}

Handler provides methods for managing CSRF tokens for a specific middleware configuration. Retrieved via HandlerFromContext.

The fields are copied from Config at construction time rather than holding a Config reference. This is intentional for immutability: the caller cannot mutate Handler state after New() returns, and the middleware closure and Handler always see consistent values without synchronization.

func HandlerFromContext

func HandlerFromContext(c *celeris.Context) *Handler

HandlerFromContext returns the Handler associated with the CSRF middleware from the request context. Returns nil if the middleware has not run.

Example
package main

import (
	"github.com/goceleris/celeris"

	"github.com/goceleris/celeris/middleware/csrf"
)

func main() {
	// Retrieve handler for more control.
	_ = func(c *celeris.Context) error {
		h := csrf.HandlerFromContext(c)
		if h != nil {
			_ = h.DeleteToken(c)
		}
		return c.String(200, "done")
	}
}

func (*Handler) DeleteToken

func (h *Handler) DeleteToken(c *celeris.Context) error

DeleteToken removes the CSRF token from server-side storage and expires the cookie. Useful for logout flows. Returns ErrTokenNotFound if no token exists in the cookie.

type MemoryStorageConfig deprecated

type MemoryStorageConfig = store.MemoryKVConfig

MemoryStorageConfig is a type alias for store.MemoryKVConfig.

Deprecated: use store.MemoryKVConfig.

type Storage deprecated

type Storage = store.KV

Storage is a type alias for store.KV — the unified byte-level key-value interface shared across celeris middleware. CSRF tokens (hex strings) are stored as their UTF-8 bytes.

For single-use token semantics (Config.SingleUseToken=true), the backend should also implement store.GetAndDeleter; backends without atomic GET+DEL fall back to a non-atomic Get+Delete pair.

Deprecated: use store.KV directly. This alias is retained for source compatibility with the pre-unified-store API.

Directories

Path Synopsis
Package memcachedstore provides a store.KV CSRF token backend built on the native celeris driver/memcached client.
Package memcachedstore provides a store.KV CSRF token backend built on the native celeris driver/memcached client.
Package redisstore provides a store.KV CSRF token backend built on the native celeris driver/redis client.
Package redisstore provides a store.KV CSRF token backend built on the native celeris driver/redis client.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL