{"record":{"id":"f25d12be746041fd","repo":"crowdsecurity/crowdsec","slug":"failed-to-seal-challenge-cookie-w","errorCode":null,"errorMessage":"failed to seal challenge cookie: %w","messagePattern":"failed to seal challenge cookie: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/appsec/challenge/challenge.go","lineNumber":725,"sourceCode":"\t\t\t\"is_bot\":  fpData.FastBotDetection,\n\t\t}).Debug(\"validated submission\")\n\t}\n\n\t// Seal the difficulty the client actually proved (MAC-authenticated above),\n\t// so the next request's re-challenge check compares against real work and an\n\t// escalated per-request difficulty survives the cookie round-trip.\n\tenvelope := &pb.ChallengeCookie{\n\t\tFingerprint:   fpData.ToProto(),\n\t\tPowDifficulty: int32(clientDifficulty),\n\t}\n\n\t// Seal under the long-lived master cookie key. The embedded not_after makes\n\t// the server validity window exactly c.cookieTTL (independent of key\n\t// rotation); the browser Max-Age below matches so both expire together.\n\tnotAfter := time.Now().Add(c.cookieTTL).Unix()\n\tcookieValue, err := sealCookieV0(envelope, c.keys.MasterCookieKey(), notAfter, 0, \"\", []byte(request.UserAgent()), c.maxCookieLen)\n\tif err != nil {\n\t\treturn nil, FingerprintData{}, 0, fmt.Errorf(\"failed to seal challenge cookie: %w\", err)\n\t}\n\n\tck := cookie.NewAppsecCookie(ChallengeCookieName).HttpOnly().Path(\"/\").SameSite(cookie.SameSiteLax).ExpiresIn(c.cookieTTL).Value(cookieValue)\n\tif request.URL.Scheme == \"https\" {\n\t\tck = ck.Secure()\n\t}\n\n\treturn ck, fpData, clientDifficulty, nil\n}\n\n// SealAllowlistCookie mints an allowlist-bypass cookie (no fingerprint, with\n// the operator reason) so GrantChallengeCookie can let trusted bots skip the\n// challenge UI while still hitting on_challenge rules via fingerprint.Allowlisted.\n// not_after honors c.cookieTTL unless ttlOverride (>0) is given; reason is\n// bounded by MaxAllowlistReasonLen (crypto.go).\nfunc (c *ChallengeRuntime) SealAllowlistCookie(request *http.Request, reason string, ttlOverride *time.Duration) (*cookie.AppsecCookie, error) {\n\tif c == nil {\n\t\treturn nil, errors.New(\"challenge runtime not initialized\")","sourceCodeStart":707,"sourceCodeEnd":743,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/appsec/challenge/challenge.go#L707-L743","documentation":"After full validation, ValidateChallengeResponse seals an AppsecCookie under the master cookie key with an embedded not_after (cookieTTL) and the request's User-Agent. If sealCookieV0 fails, the error is wrapped as 'failed to seal challenge cookie' and no cookie can be issued even though the PoW itself succeeded.","triggerScenarios":"Calling ValidateChallengeResponse when sealCookieV0 returns an error — nil/invalid master cookie key, envelope serialization failure, cookie value exceeding maxCookieLen, or invalid inputs (empty key material after misconfiguration).","commonSituations":"Misconfigured or empty master cookie key in AppSec config; a User-Agent/cookie combination producing a sealed value longer than maxCookieLen (extremely long UA headers); internal key-rotation state corruption.","solutions":["Check the AppSec configuration for a valid, non-empty cookie secret / key material and restart.","Review the wrapped error: if it reports size, increase maxCookieLen or shorten cookie contents.","If it appeared after a key-rotation change, verify keys.MasterCookieKey() is initialized before validation runs.","Report upstream with the wrapped error if it reproduces with default config — it indicates an internal invariant bug."],"exampleFix":"# before: appsec config missing cookie secret\nappsec:\n  challenge:\n    cookie_ttl: 30s\n# after: provide key material\nappsec:\n  challenge:\n    cookie_ttl: 30s\n    cookie_secret: <strong-random-secret>","handlingStrategy":"try-catch","validationCode":"// preflight in config loading: reject empty cookie key early\nif len(keys.MasterCookieKey()) == 0 {\n    return errors.New(\"appsec: master cookie key is empty; cannot seal challenge cookies\")\n}","typeGuard":null,"tryCatchPattern":"cookie, fp, diff, err := rt.ValidateChallengeResponse(req, body)\nif err != nil && strings.Contains(err.Error(), \"seal challenge cookie\") {\n    logger.WithError(err).Error(\"cookie sealing failed; check cookie key config and maxCookieLen\")\n    http.Error(w, \"internal error\", http.StatusInternalServerError)\n    return\n}","preventionTips":["Configure a strong, non-empty cookie secret before enabling challenges.","Validate config at load time so a missing key fails fast.","Keep maxCookieLen generous enough for typical User-Agent headers.","Treat this as server-side: it never indicates an invalid client."],"tags":["appsec","challenge","cookie","crypto"],"backgroundTag":"cookie-signing-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"}