gofiber/fiber · critical

CSRF: Chained extractor reads from the same cookie '${Cookie

Error message

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

What it means

Same protection as error 266 but applied to a chained/nested extractor tree (csrf/config.go:194-199). Extractor.Contains walks the full chain (e.g. a FromHeader fallback to FromCookie) and panics if any node in the chain reads from the CSRF storage cookie, because a fallback path that reads the storage cookie still lets an attacker submit a valid token.

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 9a4c7e57fe)

Solutions

  1. Remove any FromCookie node whose Key equals CookieName from the chain; keep only header/body/query sources.
  2. If a cookie fallback is required, name it differently and populate it from the token explicitly in your handler, never from the storage cookie.
  3. Inspect the chain with Extractor.Contains at test time to assert no node reads CookieName.

Example fix

// before
Extractor: extractors.Chain(
    extractors.FromHeader("X-Csrf-Token"),
    extractors.FromCookie("csrf_"), // matches CookieName -> panic
),

// after
Extractor: extractors.Chain(
    extractors.FromHeader("X-Csrf-Token"),
    extractors.FromForm("csrf_token"),
)
Defensive patterns

Strategy: validation

Validate before calling

func validateCSRFExtractorTree(cfg csrf.Config) error {
    insecure := cfg.Extractor.Contains(func(e extractors.Extractor) bool {
        return e.Source == extractors.SourceCookie && e.Key == cfg.CookieName
    })
    if insecure {
        return fmt.Errorf("extractor chain reads storage cookie %q", cfg.CookieName)
    }
    return nil
}

if err := validateCSRFExtractorTree(cfg); err != nil { log.Fatal(err) }

Prevention

When it happens

Trigger: Composing extractors with a chain whose fallback (or primary) node is FromCookie(CookieName), e.g. extractors.Chain(FromHeader("X-Csrf-Token"), FromCookie("csrf_")). The Contains walk finds the insecure node even though it is not the top-level extractor.

Common situations: Defensive devs add multiple extraction sources so clients can send the token in any convenient way, unintentionally including the storage cookie as a fallback. CI usually catches this only after deploy because local tests pass with the header path.

Related errors


AI-assisted analysis of gofiber/fiber@9a4c7e57fe (2026-08-04). Data as JSON: /data/errors/9528266c7525c443.json. Report an issue: GitHub.