{"id":"9528266c7525c443","repo":"gofiber/fiber","slug":"csrf-chained-extractor-reads-from-the-same-cookie","errorCode":null,"errorMessage":"CSRF: Chained extractor reads from the same cookie '${CookieName}' used for token storage. This completely defeats CSRF protection.","messagePattern":"CSRF: Chained extractor reads from the same cookie '(.+?)' used for token storage\\. This completely defeats CSRF protection\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"middleware/csrf/config.go","lineNumber":197,"sourceCode":"}\n\n// validateExtractorSecurity checks for insecure extractor configurations\nfunc validateExtractorSecurity(cfg *Config) {\n\tif cfg == nil {\n\t\treturn\n\t}\n\t// Check primary extractor\n\tif isInsecureCookieExtractor(cfg.Extractor, cfg.CookieName) {\n\t\tpanic(\"CSRF: Extractor reads from the same cookie '\" + cfg.CookieName +\n\t\t\t\"' used for token storage. This completely defeats CSRF protection.\")\n\t}\n\n\t// Check the full extractor tree so a nested chain cannot hide a fallback\n\t// that reads from the CSRF storage cookie.\n\tif cfg.Extractor.Contains(func(extractor extractors.Extractor) bool {\n\t\treturn isInsecureCookieExtractor(extractor, cfg.CookieName)\n\t}) {\n\t\tpanic(\"CSRF: Chained extractor reads from the same cookie '\" + cfg.CookieName +\n\t\t\t\"' used for token storage. This completely defeats CSRF protection.\")\n\t}\n\n\t// Additional security warnings (non-fatal)\n\tif cfg.Extractor.Source == extractors.SourceQuery || cfg.Extractor.Source == extractors.SourceParam {\n\t\tlog.Warnf(\"[CSRF WARNING] Using %v extractor - URLs may be logged\", cfg.Extractor.Source)\n\t}\n}\n\n// isInsecureCookieExtractor checks if an extractor unsafely reads from the CSRF cookie\nfunc isInsecureCookieExtractor(extractor extractors.Extractor, cookieName string) bool {\n\tif extractor.Source == extractors.SourceCookie {\n\t\t// Exact match - definitely insecure\n\t\tif extractor.Key == cookieName {\n\t\t\treturn true\n\t\t}\n\n\t\t// Case-insensitive match - potentially confusing, warn but don't panic","sourceCodeStart":179,"sourceCodeEnd":215,"githubUrl":"https://github.com/gofiber/fiber/blob/9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c/middleware/csrf/config.go#L179-L215","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Remove any FromCookie node whose Key equals CookieName from the chain; keep only header/body/query sources.","If a cookie fallback is required, name it differently and populate it from the token explicitly in your handler, never from the storage cookie.","Inspect the chain with Extractor.Contains at test time to assert no node reads CookieName."],"exampleFix":"// before\nExtractor: extractors.Chain(\n    extractors.FromHeader(\"X-Csrf-Token\"),\n    extractors.FromCookie(\"csrf_\"), // matches CookieName -> panic\n),\n\n// after\nExtractor: extractors.Chain(\n    extractors.FromHeader(\"X-Csrf-Token\"),\n    extractors.FromForm(\"csrf_token\"),\n)","handlingStrategy":"validation","validationCode":"func validateCSRFExtractorTree(cfg csrf.Config) error {\n    insecure := cfg.Extractor.Contains(func(e extractors.Extractor) bool {\n        return e.Source == extractors.SourceCookie && e.Key == cfg.CookieName\n    })\n    if insecure {\n        return fmt.Errorf(\"extractor chain reads storage cookie %q\", cfg.CookieName)\n    }\n    return nil\n}\n\nif err := validateCSRFExtractorTree(cfg); err != nil { log.Fatal(err) }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Avoid chaining multiple extractor sources unless necessary; each node widens attack surface.","Review every node of a chained extractor for cookie-name collisions before deploy."],"tags":["csrf","security","extractor","chain","cookie","panic"],"analyzedSha":"9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c","analyzedAt":"2026-08-04T21:44:03.395Z","schemaVersion":2}