crowdsecurity/crowdsec · warning
invalid challenge cookie: %w
Error message
invalid challenge cookie: %w
What it means
ValidCookie unseals and validates the client's challenge cookie (AES-GCM envelope integrity, expiration, user-agent pinning). This wrapper means openCookie rejected the cookie for any reason: malformed base64, bad GCM signature, expired not_after, unknown version, oversized value, or a user-agent mismatch breaking the AEAD authentication.
Source
Thrown at pkg/appsec/challenge/challenge.go:787
Fingerprint FingerprintData
PowDifficulty int
Allowlisted bool
AllowlistReason string
}
// ValidCookie unseals and validates a challenge cookie: envelope (version,
// AES-GCM tag), not_after expiry, and UA-pinning (a stolen cookie is useless to
// a different client). On any failure (tampered/expired/UA-mismatch/unknown
// version) it returns an error and the caller should treat the request as
// cookieless.
func (c *ChallengeRuntime) ValidCookie(ck *http.Cookie, userAgent string) (*CookieData, error) {
if ck == nil {
return nil, errors.New("nil cookie")
}
envelope, err := openCookie(ck.Value, c.keys.MasterCookieKey(), []byte(userAgent), c.maxCookieLen)
if err != nil {
return nil, fmt.Errorf("invalid challenge cookie: %w", err)
}
return &CookieData{
Fingerprint: fingerprintDataFromProto(envelope.Envelope.GetFingerprint()),
PowDifficulty: int(envelope.Envelope.GetPowDifficulty()),
Allowlisted: envelope.Allowlisted,
AllowlistReason: envelope.AllowlistReason,
}, nil
}
View on GitHub (pinned to 909b515798)
Solutions
- Treat this as 'no valid cookie': the caller should serve the challenge again rather than error out to the client.
- Ensure all instances in a distributed deployment share the identical master_secret config value.
- Check whether the User-Agent differs between cookie mint and validation; the UA is AAD-pinned to the cookie.
- Confirm the cookie is within cookie_ttl; expired cookies must be re-minted by solving the challenge.
- If errors appear fleet-wide after a restart, configure a persistent master_secret so cookies survive restarts.
Example fix
ck, err := r.Cookie(challenge.ChallengeCookieName)
if err != nil {
return serveChallenge(w, req)
}
cd, err := rt.ValidCookie(ck, req.UserAgent())
if err != nil {
log.Debug().Err(err).Msg("invalid challenge cookie, re-challenging")
return serveChallenge(w, req)
} Defensive patterns
Strategy: fallback
Validate before calling
if ck == nil || ck.Value == "" || len(ck.Value) > challenge.MaxCookieLen {
return serveChallenge(w, req)
} Type guard
func validChallengeCookie(ck *http.Cookie) bool { return ck != nil && ck.Value != "" && len(ck.Value) <= challenge.MaxCookieLen } Try / catch
cd, err := rt.ValidCookie(ck, req.UserAgent())
if err != nil {
log.Debug().Err(err).Msg("invalid challenge cookie; serving challenge")
return serveChallenge(w, req)
} Prevention
- Set one identical master_secret across all fleet instances.
- Set a persistent master_secret so cookies survive restarts.
- Expect UA-pinning: never validate a cookie against a rewritten User-Agent.
- Treat cookie errors as 'no cookie' and re-challenge, not as fatal.
When it happens
Trigger: Calling ChallengeRuntime.ValidCookie(cookie, userAgent) with a cookie that is expired (not_after <= now), tampered with, sealed under a different master_secret, sent from a different User-Agent than when minted, corrupt, larger than maxCookieLen, or using an unknown version byte.
Common situations: Client browsers holding a cookie older than cookie_ttl (default 12h); load-balanced fleets where instances have different master_secret values; cookie replayed from a different browser/UA than the one that solved the challenge; shared master_secret rotated on only some nodes; stale cookies surviving an application restart when master_secret is ephemeral.
Related errors
- cookie expired
- unknown cookie version
- allowlist reason exceeds maximum length
- cookie exceeds maximum size
- failed to seal challenge cookie: %w
AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06).
Data as JSON: /api/errors/e72cd05ae8ce9025.
Report an issue: GitHub.