{"record":{"id":"c10df5d585e7aada","repo":"crowdsecurity/crowdsec","slug":"errcookiepayload","errorCode":"ErrCookiePayload","errorMessage":"%w: %w","messagePattern":"%w: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/appsec/challenge/crypto.go","lineNumber":268,"sourceCode":"\tif reasonLen > MaxAllowlistReasonLen {\n\t\treturn nil, fmt.Errorf(\"%w: reason_len=%d\", ErrCookieMalformed, reasonLen)\n\t}\n\n\tif len(plaintext) < cookiePlaintextFixedHeaderLen+reasonLen {\n\t\treturn nil, fmt.Errorf(\"%w: plaintext shorter than declared reason_len\", ErrCookieMalformed)\n\t}\n\n\tif notAfter <= now.Unix() {\n\t\treturn nil, fmt.Errorf(\"%w: not_after=%d now=%d\", ErrCookieExpired, notAfter, now.Unix())\n\t}\n\n\treasonStart := cookiePlaintextFixedHeaderLen\n\treasonEnd := reasonStart + reasonLen\n\treason := string(plaintext[reasonStart:reasonEnd])\n\n\tenvelope := &pb.ChallengeCookie{}\n\tif err := proto.Unmarshal(plaintext[reasonEnd:], envelope); err != nil {\n\t\treturn nil, fmt.Errorf(\"%w: %w\", ErrCookiePayload, err)\n\t}\n\n\treturn &CookieEnvelope{\n\t\tEnvelope:        envelope,\n\t\tAllowlisted:     flags&cookieFlagAllowlisted != 0,\n\t\tAllowlistReason: reason,\n\t\tNotAfter:        notAfter,\n\t}, nil\n}\n","sourceCodeStart":250,"sourceCodeEnd":278,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/appsec/challenge/crypto.go#L250-L278","documentation":"After extracting the allowlist reason, openCookieV0Bytes unmarshals the remaining plaintext bytes as a pb.ChallengeCookie protobuf. If proto.Unmarshal fails, the tail of the plaintext is not a valid ChallengeCookie message and the error is wrapped in ErrCookiePayload. This indicates an incompatible, truncated, or forged cookie payload.","triggerScenarios":"openCookie is called with a cookie whose trailing bytes after the reason section are not a serialized pb.ChallengeCookie — e.g. produced by a binary with a different protobuf schema, or a truncated/forged cookie.","commonSituations":"Mixed CrowdSec versions where the cookie payload schema differs; cookies corrupted in transit or by storage; an attacker-supplied cookie value; a build where embedded protobuf definitions are out of sync.","solutions":["Regenerate the cookie from a matching (same-version) CrowdSec instance","Ensure all instances sharing master_secret run compatible versions with identical ChallengeCookie proto definitions","Do not attempt to parse attacker-controlled cookies further; treat as invalid and issue a new challenge","If you control the issuer, re-seal with the current proto schema"],"exampleFix":"// before\nenv, err := rt.OpenCookie(raw, now)\nif err != nil { http.Error(w, \"bad cookie\", 400) }\n// after\nif errors.Is(err, challenge.ErrCookiePayload) {\n    log.Debug().Err(err).Msg(\"invalid cookie payload; issuing new challenge\")\n    return issueNewChallenge(w, r)\n}","handlingStrategy":"try-catch","validationCode":"if errors.Is(err, challenge.ErrCookiePayload) { /* invalid payload */ }","typeGuard":"func isCookiePayloadError(err error) bool { return errors.Is(err, challenge.ErrCookiePayload) }","tryCatchPattern":"env, err := rt.OpenCookie(raw, now)\nif errors.Is(err, challenge.ErrCookiePayload) {\n    log.Debug().Msg(\"invalid cookie payload; rejecting and re-issuing\")\n    return issueNewChallenge()\n}","preventionTips":["Deploy matching versions across all instances sharing a master secret","Never parse attacker-supplied cookies beyond errors.Is classification","Keep generated protobuf code in sync with the schema","Reject cookies from unknown issuers early"],"tags":["appsec","protobuf","cookie-parsing"],"backgroundTag":"protobuf-unmarshal-failed","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}