csrf

package
v0.0.26 Latest Latest
Warning

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

Go to latest
Published: Sep 24, 2026 License: MIT Imports: 19 Imported by: 1

README

CSRF Middleware

What it does

Protects browser session-based applications from cross-site request forgery using tokens and origin checks.

How to implement

package main

import (
	"github.com/oarkflow/fh"
	"github.com/oarkflow/fh/mw/csrf"
)

func main() {
	app := fh.New()
	app.Use(csrf.New(csrf.Config{}))

	app.Get("/", func(c fh.Ctx) error {
		return c.Status(fh.StatusOK).SendString("ok")
	})
}

Impact

Adds token validation to unsafe methods. Essential for cookie-authenticated browser apps.

Ordering guidance

Run after session/cookie middleware and before handlers for POST/PUT/PATCH/DELETE.

Production considerations

The default cookie is Secure and unsafe requests must include a valid Origin or Referer plus the token. AllowInsecureCookie and AllowMissingOrigin are explicit compatibility opt-outs and should not be used on public browser-session routes. Use SameSite cookies and HTTPS. APIs using bearer tokens generally do not need CSRF, but browser cookie flows do.

Documentation

Overview

Package csrf implements a signed, browser-session-bound, origin-aware double-submit CSRF defense for fh.

Security properties:

  • HMAC-SHA256 signed CSRF tokens with signing-key rotation.
  • Tokens are bound to an HttpOnly browser-binding cookie and may also be bound to an application/authentication session via SessionBinding.
  • Exact scheme + host + effective-port Origin/Referer validation.
  • Fetch Metadata validation (Sec-Fetch-Site) as defense in depth.
  • Strict Origin validation for WebSocket handshakes.
  • Secure, SameSite=Lax, __Host- cookies by default.
  • Constant-time comparison of submitted and cookie tokens.
  • Header and HTML form token extraction; custom extraction is supported.

For horizontally scaled production deployments, configure Secret or Keys so every instance shares the same signing material. If neither is configured, a cryptographically random process-local key is used; that is secure for a single process but tokens will not survive restarts or work across replicas.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrInvalidConfig = errors.New("csrf: invalid configuration")
	ErrInvalidToken  = errors.New("csrf: invalid token")
)
View Source
var DefaultConfig = defaultConfig()

DefaultConfig is a compatibility snapshot of the defaults.

Deprecated: New does not read this mutable variable; mutating it does not alter middleware defaults. This prevents global mutation/races from silently weakening security. Construct Config values explicitly instead.

Functions

func New

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

New returns CSRF middleware and preserves the historical fh middleware API. Invalid configuration panics at application startup rather than silently running with weakened protection. Use NewWithError when explicit error handling is preferred.

func NewWithError

func NewWithError(config ...Config) (fh.HandlerFunc, error)

NewWithError validates config and returns a middleware handler.

func RotateToken

func RotateToken(c fh.Ctx, config ...Config) (string, error)

RotateToken preserves the historical package-level API. For repeated use, prefer constructing a Protector once and calling protector.RotateToken(c).

func Token

func Token(c fh.Ctx, config ...Config) string

Token returns the token stored by middleware for the current request, or the request cookie when middleware has not populated the local yet.

Types

type Config

type Config struct {
	CookieName   string
	HeaderName   string
	FormField    string
	CookiePath   string
	CookieDomain string
	CookieSecure bool

	// AllowInsecureCookie is required to disable Secure on CSRF cookies. This
	// explicit opt-out prevents a partial Config literal from silently weakening
	// the secure default. Intended for loopback/local development only.
	AllowInsecureCookie bool

	CookieSameSite fh.SameSite

	// CookieMaxAge controls both CSRF cookies. Zero creates session cookies.
	// A session cookie is the default so CSRF lifetime does not accidentally
	// outlive or conflict with an application's authentication lifetime.
	CookieMaxAge time.Duration

	// BindingCookieName names the HttpOnly random browser-session binding cookie.
	// It is included in the CSRF-token MAC and prevents cookie-injection/fixation
	// attacks against a naive double-submit construction.
	BindingCookieName string

	// DisableBrowserBinding explicitly disables the HttpOnly browser-binding
	// cookie. This weakens the default and should normally be used only when a
	// strong SessionBinding is always available.
	DisableBrowserBinding bool

	// Secret is a convenient single active key. For key rotation, prefer Keys.
	// If both Secret and Keys are configured, configuration is rejected.
	Secret []byte

	// Keys enables signing-key rotation. The first key signs; every key verifies.
	Keys []Key

	// SessionBinding optionally binds tokens to the application's auth/session
	// identity in addition to the default browser-session binding cookie.
	SessionBinding SessionBinding

	// TrustedOrigins are exact additional origins allowed to submit unsafe
	// requests, e.g. "https://app.example.com". Wildcards are intentionally not
	// supported.
	TrustedOrigins []string

	// TargetOrigin pins the browser-visible target origin. It is strongly
	// recommended behind TLS-terminating reverse proxies/load balancers.
	TargetOrigin string

	// TargetOriginResolver is the dynamic equivalent of TargetOrigin. Configure
	// at most one of TargetOrigin and TargetOriginResolver.
	TargetOriginResolver TargetOriginResolver

	// RequireOriginHeader is retained for source compatibility. The secure
	// default is true. Because bool zero-values cannot express an explicit false
	// in a partial Config, use AllowMissingOrigin to opt out.
	RequireOriginHeader bool

	// AllowMissingOrigin permits unsafe requests carrying neither Origin nor
	// Referer. Prefer bypassing CSRF on authenticated non-browser routes instead.
	AllowMissingOrigin bool

	// AllowUntrustedOrigin disables Origin/Referer and Fetch Metadata enforcement
	// while retaining token verification. Intended only for local development.
	AllowUntrustedOrigin bool

	// CheckFetchMetadata is retained as an enable switch. It is enabled by
	// default. Use DisableFetchMetadata for an explicit opt-out.
	CheckFetchMetadata bool

	// DisableFetchMetadata explicitly disables Sec-Fetch-Site validation.
	DisableFetchMetadata bool

	// ProtectWebSockets enables strict Origin/Fetch-Metadata checks for WebSocket
	// handshakes even though HTTP GET is normally a safe method. Enabled by
	// default. Browser WebSocket APIs cannot reliably attach the CSRF header, so
	// WebSocket protection is origin-based rather than token-header-based.
	ProtectWebSockets bool

	// AllowUnprotectedWebSockets explicitly disables the secure WebSocket default.
	AllowUnprotectedWebSockets bool

	// AutoRotateOnChange automatically re-issues the CSRF token when the
	// session binding changes (for example after login, logout, or account
	// switching). When true, the middleware detects binding changes by
	// comparing a short HMAC fingerprint stored in a companion cookie and
	// calls RotateToken transparently. This closes the gap where a
	// pre-authentication CSRF token could remain valid after the session
	// identity changes.
	AutoRotateOnChange bool

	// Extractor overrides HeaderName/FormField extraction when non-nil.
	Extractor TokenExtractor

	// Next bypasses this middleware for selected routes. Prefer this for
	// authenticated machine-to-machine/non-browser APIs instead of weakening
	// global origin policy.
	Next func(fh.Ctx) bool
}

Config controls CSRF protection.

type Key

type Key struct {
	ID     string
	Secret []byte
}

Key is an HMAC signing key. The first configured key signs newly-issued tokens. All configured keys verify existing tokens, allowing safe rotation.

ID is embedded in the token and must consist only of ASCII letters, digits, '-' or '_'. Secret must be at least 32 bytes.

type Protector

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

Protector is an immutable, validated CSRF configuration. It can be reused by middleware and explicit token-rotation calls.

func NewProtector

func NewProtector(config ...Config) (*Protector, error)

NewProtector constructs an immutable Protector for middleware and explicit rotation/clearing operations.

func (*Protector) Clear

func (p *Protector) Clear(c fh.Ctx)

Clear expires both CSRF cookies using the configured path/domain/security attributes. It is useful on logout/account switching.

func (*Protector) Middleware

func (p *Protector) Middleware(c fh.Ctx) error

Middleware enforces CSRF protection.

func (*Protector) RotateToken

func (p *Protector) RotateToken(c fh.Ctx) (string, error)

RotateToken issues a fresh CSRF token using the Protector's exact immutable configuration. Call this after authentication/session transitions.

type SessionBinding

type SessionBinding func(fh.Ctx) ([]byte, error)

SessionBinding returns stable, non-secret bytes identifying the current application/authentication session. When configured, CSRF tokens become invalid automatically when that binding changes (for example after login, logout, account switching, or session-ID regeneration).

Do not return a password, access token, or other credential. A random internal session ID is ideal. The binding is used only as HMAC input and is never sent to the client by this package.

type TargetOriginResolver

type TargetOriginResolver func(fh.Ctx) (string, error)

TargetOriginResolver returns the externally-visible target origin for the current request, such as "https://example.com". Use this behind a trusted reverse proxy when c.BaseURL() reflects the internal hop rather than the browser-visible scheme/host. The returned value must contain only an origin: scheme + host + optional port, with no path/query/fragment/userinfo.

Do not blindly copy Forwarded/X-Forwarded-* headers in this callback unless a trusted-proxy middleware has already validated and sanitized them.

type TokenExtractor

type TokenExtractor func(fh.Ctx) (string, error)

TokenExtractor overrides the default request-token extraction logic. It is useful for custom body formats. Tokens should never be accepted from URL query parameters because URLs commonly leak into logs, browser history and Referer.

Jump to

Keyboard shortcuts

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