{"record":{"id":"e64be18800343a6e","repo":"crowdsecurity/crowdsec","slug":"cookie-exceeds-maximum-size","errorCode":null,"errorMessage":"cookie exceeds maximum size","messagePattern":"cookie exceeds maximum size","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/appsec/challenge/crypto.go","lineNumber":34,"sourceCode":"\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"golang.org/x/crypto/hkdf\"\n\n\t\"github.com/crowdsecurity/crowdsec/pkg/appsec/challenge/pb\"\n\t\"google.golang.org/protobuf/proto\"\n)\n\nvar (\n\tErrCookieMalformed     = errors.New(\"malformed cookie\")\n\tErrCookieSignature     = errors.New(\"invalid cookie signature\")\n\tErrCookiePayload       = errors.New(\"invalid cookie payload\")\n\tErrCookieExpired       = errors.New(\"cookie expired\")\n\tErrCookieVersion       = errors.New(\"unknown cookie version\")\n\tErrAllowlistReasonSize = errors.New(\"allowlist reason exceeds maximum length\")\n\tErrCookieTooLarge      = errors.New(\"cookie exceeds maximum size\")\n)\n\nconst hkdfInfo = \"crowdsec-challenge-cookie\"\n\n// MaxAllowlistReasonLen caps the reason string operators pass to\n// GrantChallengeCookie. The reason travels inside every Set-Cookie + Cookie\n// header round-trip until the cookie expires; bounding it keeps the cookie\n// well under the 4 KB browser limit even with the AES-GCM tag + base64\n// expansion.\nconst MaxAllowlistReasonLen = 256\n\n// MaxCookieLen is the DEFAULT per-cookie size (RFC 6265 §6.1: 4096 bytes).\n// Can be configured via Config.MaxCookieSize and we reject anything bigger.\nconst MaxCookieLen = 4096\n\n// Cookie wire format. A single version byte at offset 0 lets us evolve the\n// format without flag-day-style cookie invalidation. New formats add a new\n// case in openCookie's switch.","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/appsec/challenge/crypto.go#L16-L52","documentation":"ErrCookieTooLarge means the serialized challenge cookie would exceed the configured maximum cookie length. sealCookieV0 checks the plaintext (fixed header + reason + allowlist envelope) against the budget derived from maxCookieLen, and openCookie rejects base64-encoded values whose length exceeds maxCookieLen. This protects browsers and intermediaries that reject oversized Set-Cookie headers.","triggerScenarios":"sealCookieV0: plaintext size (cookiePlaintextFixedHeaderLen + reason + protobuf envelope) exceeds maxCookieLen/4*3-1-nonce-overhead (crypto.go:135). openCookie: base64 length of the presented value exceeds maxCookieLen (crypto.go:195). Exercised by TestCookieV0_SealEnvelopeTooLarge, TestCookieV0_OpenValueTooLarge, TestCookieV0_ConfigurableLimit.","commonSituations":"Very long allowlist reasons or large allowlist metadata inflate the envelope; a client sends a cookie value larger than the configured limit (possibly issued under a larger maxCookieLen config); misconfigured maxCookieLen that is too small for the payload.","solutions":["Reduce the embedded data: shorten the allowlist reason or trim envelope entries.","Raise the configured maxCookieLen (it is configurable — see TestCookieV0_ConfigurableLimit), keeping browser ~4KB header limits in mind.","If the presented cookie is oversized relative to config, confirm all nodes use the same maxCookieLen; issue a fresh cookie.","Check for clients sending concatenated/duplicated cookie values through proxy rewriting."],"exampleFix":"// before: sealing with a huge reason inside the envelope\nsealCookieV0(c, key, notAfter, flag, giantReason, ua, maxCookieLen)\n// after: cap or reject early\nif len(giantReason) > challenge.MaxAllowlistReasonLen {\n    return fmt.Errorf(\"reason too long\")\n}\nsealCookieV0(c, key, notAfter, flag, giantReason, ua, maxCookieLen)","handlingStrategy":"validation","validationCode":"// estimate before sealing: fixed header + reason + envelope vs budget\nbudget := (maxCookieLen*3/4) - 1 - 16 /*nonce*/ - 16 /*gcm tag*/\nif cookiePlaintextFixedHeaderLen+len(reason)+estimatedEnvelope > budget {\n    return errors.New(\"cookie payload would exceed maxCookieLen\")\n}","typeGuard":null,"tryCatchPattern":"encoded, err := sealCookieV0(c, key, notAfter, flag, reason, ua, maxLen)\nif errors.Is(err, challenge.ErrCookieTooLarge) {\n    // shrink payload or raise maxCookieLen, then retry once\n    encoded, err = sealCookieV0(c, key, notAfter, flag, truncate(reason), ua, maxLen)\n}\nreturn err","preventionTips":["Cap allowlist reasons and envelope entries well below the size budget","Keep maxCookieLen consistent across all nodes issuing cookies","Remember browser Set-Cookie practical limits (~4KB)","Test with worst-case payloads (longest reason + max envelope)"],"tags":["appsec","cookie","size-limit","validation"],"backgroundTag":"payload-too-large","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}