{"record":{"id":"721026db84be21c1","repo":"gofiber/fiber","slug":"csrf-extractor-reads-from-the-same-cookie-cook","errorCode":null,"errorMessage":"CSRF: Extractor reads from the same cookie '${CookieName}' used for token storage. This completely defeats CSRF protection.","messagePattern":"CSRF: 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":188,"sourceCode":"\t}\n\t// Check if Extractor is zero value (since it's a struct)\n\tif cfg.Extractor.Extract == nil {\n\t\tcfg.Extractor = ConfigDefault.Extractor\n\t}\n\t// Validate extractor security configurations\n\tvalidateExtractorSecurity(&cfg)\n\n\treturn cfg\n}\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","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/gofiber/fiber/blob/a105acad6c1e4576a77f01e02973f67e962bb58d/middleware/csrf/config.go#L170-L206","documentation":"The CSRF middleware stores its token in cfg.CookieName and reads it from requests via cfg.Extractor. If the primary extractor's Source is extractors.SourceCookie and it reads the SAME cookie name used for token storage, the middleware would echo the stored token to any requester, completely defeating CSRF protection — so this configuration is rejected at startup. The check (isInsecureCookieExtractor) compares the extractor's CookieName against cfg.CookieName.","triggerScenarios":"csrf.New(csrf.Config{ CookieName: \"csrf\", Extractor: extractors.Cookie(\"csrf\") }) — i.e. the extractor is told to pull the token from the very cookie the middleware uses to store it. Any SourceCookie extractor whose target name matches cfg.CookieName triggers the panic.","commonSituations":"Switching the extractor source to Cookie for an SPA that sends the token in a cookie, but forgetting the storage cookie must be HttpOnly/separate from the readable token; copy-paste where both fields get the same name; renaming the storage cookie without updating the extractor.","solutions":["Read the CSRF token from a DIFFERENT source than the storage cookie — typically a header: extractors.Header(\"X-CSRF-Token\"), or a form field, or a separate readable cookie with a different name.","Keep the storage cookie (CookieName) HttpOnly and never point an extractor at it.","If you need a double-submit cookie pattern, use two distinct cookie names: one HttpOnly storage cookie and one readable cookie for the extractor."],"exampleFix":"// before\ncsrf.New(csrf.Config{\n    CookieName: \"csrf_token\",\n    Extractor:  extractors.Cookie(\"csrf_token\"),\n})\n// after\ncsrf.New(csrf.Config{\n    CookieName: \"csrf_token\",          // HttpOnly storage\n    Extractor:  extractors.Header(\"X-CSRF-Token\"),\n})","handlingStrategy":"validation","validationCode":"// Ensure the extractor does not read from the storage cookie.\nfunc extractorReadsStorageCookie(ex extractors.Extractor, storage string) bool {\n    return ex != nil && ex.Source == extractors.SourceCookie && ex.CookieName == storage\n}\nif extractorReadsStorageCookie(cfg.Extractor, cfg.CookieName) {\n    return errors.New(\"CSRF extractor must not read the storage cookie\")\n}","typeGuard":null,"tryCatchPattern":"defer func() {\n    if r := recover(); r != nil {\n        log.Fatalf(\"insecure CSRF extractor: %v\", r)\n    }\n}()\ncsrf.New(cfg)","preventionTips":["Read the CSRF token from a header or a separate readable cookie, never the HttpOnly storage cookie.","Keep CookieName and the extractor target name distinct by convention (e.g. 'csrf' vs 'csrf_readable').","In review, flag any SourceCookie extractor whose name equals CookieName."],"tags":["middleware","csrf","security","extractor","startup"],"backgroundTag":null,"analyzedSha":"a105acad6c1e4576a77f01e02973f67e962bb58d","analyzedAt":"2026-08-11T17:33:26.942Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}