gofiber/fiber · critical

CSRF: Chained extractor reads from the same cookie

Error message

CSRF: Chained extractor reads from the same cookie '${CookieName}' used for token storage. This completely defeats CSRF protection.

What it means

Even when the top-level CSRF extractor is not the storage cookie, fiber walks the entire extractor tree via Extractor.Contains(...) and panics if ANY nested extractor in a chain (e.g. a Fallback, First, or Chain) reads from the storage cookie. This prevents hiding an insecure fallback behind a secure primary extractor. The predicate reuses isInsecureCookieExtractor against each node's Source/CookieName.

Solutions

  1. Audit every leaf of a composite extractor: no SourceCookie leaf may target cfg.CookieName.
  2. Use a distinct readable cookie name for any cookie-based fallback (separate from the HttpOnly storage cookie).
  3. Prefer a header/form primary and a non-storage-cookie fallback; remove cookie fallbacks that point at the storage cookie.

Example fix

// before
csrf.New(csrf.Config{
    CookieName: "csrf",
    Extractor: extractors.First(
        extractors.Header("X-CSRF-Token"),
        extractors.Cookie("csrf"), // insecure fallback
    ),
})
// after
csrf.New(csrf.Config{
    CookieName: "csrf",
    Extractor: extractors.First(
        extractors.Header("X-CSRF-Token"),
        extractors.Cookie("csrf_readable"), // different cookie
    ),
})
Defensive patterns

Strategy: validation

Validate before calling

// Walk the extractor tree and reject any leaf reading the storage cookie.
if cfg.Extractor.Contains(func(e extractors.Extractor) bool {
    return e != nil && e.Source == extractors.SourceCookie && e.CookieName == cfg.CookieName
}) {
    return errors.New("CSRF extractor chain must not read the storage cookie")
}

Try / catch

defer func() {
    if r := recover(); r != nil {
        log.Fatalf("insecure CSRF extractor chain: %v", r)
    }
}()
csrf.New(cfg)

Prevention

When it happens

Trigger: csrf.New(csrf.Config{ CookieName: "csrf", Extractor: extractors.Chain(extractors.Header("X-CSRF-Token"), extractors.Cookie("csrf")) }) — a chain whose primary is safe but whose fallback reads the storage cookie. Also any composite (First, Fallback) that includes a SourceCookie node matching cfg.CookieName.

Common situations: Building a tolerant extractor that tries a header first and falls back to a cookie, accidentally naming the fallback cookie the same as the storage cookie; chaining third-party extractors that include a cookie reader; refactoring the extractor tree without re-checking every leaf.

Related errors


AI-assisted analysis of gofiber/fiber@a105acad6c (2026-08-11). Data as JSON: /api/errors/9528266c7525c443. Report an issue: GitHub.

Appendix: source

Thrown at middleware/csrf/config.go:197

}

// validateExtractorSecurity checks for insecure extractor configurations
func validateExtractorSecurity(cfg *Config) {
	if cfg == nil {
		return
	}
	// Check primary extractor
	if isInsecureCookieExtractor(cfg.Extractor, cfg.CookieName) {
		panic("CSRF: Extractor reads from the same cookie '" + cfg.CookieName +
			"' used for token storage. This completely defeats CSRF protection.")
	}

	// Check the full extractor tree so a nested chain cannot hide a fallback
	// that reads from the CSRF storage cookie.
	if cfg.Extractor.Contains(func(extractor extractors.Extractor) bool {
		return isInsecureCookieExtractor(extractor, cfg.CookieName)
	}) {
		panic("CSRF: Chained extractor reads from the same cookie '" + cfg.CookieName +
			"' used for token storage. This completely defeats CSRF protection.")
	}

	// Additional security warnings (non-fatal)
	if cfg.Extractor.Source == extractors.SourceQuery || cfg.Extractor.Source == extractors.SourceParam {
		log.Warnf("[CSRF WARNING] Using %v extractor - URLs may be logged", cfg.Extractor.Source)
	}
}

// isInsecureCookieExtractor checks if an extractor unsafely reads from the CSRF cookie
func isInsecureCookieExtractor(extractor extractors.Extractor, cookieName string) bool {
	if extractor.Source == extractors.SourceCookie {
		// Exact match - definitely insecure
		if extractor.Key == cookieName {
			return true
		}

		// Case-insensitive match - potentially confusing, warn but don't panic

View on GitHub (pinned to a105acad6c)