crowdsecurity/crowdsec · error
invalid ticket in challenge response
Error message
invalid ticket in challenge response
What it means
Raised in ValidateChallengeResponse when the ticket/nonce material (the 'r' field) fails ticket validation — e.g. the ticket is expired, unknown to this runtime, or malformed. Signals a stale or replayed challenge session rather than a client-side computation failure.
Source
Thrown at pkg/appsec/challenge/challenge.go:640
clientNonce := vars.Get("n")
clientPowSalt := vars.Get("p")
clientPowMAC := vars.Get("m")
clientDifficultyStr := vars.Get("d")
clientPath := vars.Get("u")
if encryptedFingerprint == "" || clientR == "" || clientTS == "" || clientSig == "" || clientNonce == "" || clientPowSalt == "" || clientPowMAC == "" || clientDifficultyStr == "" || clientPath == "" {
return nil, FingerprintData{}, 0, errors.New("missing required fields in challenge response")
}
// Override the request path so the WAF sees the original URL the client
request.URL.Path = clientPath
// The difficulty the client claims it solved. It is untrusted until the PoW
// MAC (which binds it) is verified in verifyChallenge below. Bound to the
// valid PoW range so a malformed value can't reach the PoW/seal logic.
clientDifficulty, err := strconv.Atoi(clientDifficultyStr)
if err != nil || clientDifficulty < PowDifficultyDisabled || clientDifficulty > PowDifficultyImpossible {
return nil, FingerprintData{}, 0, errors.New("invalid ticket in challenge response")
}
// Server-issued `r` is a 16-byte nonce in hex (generateChallengeNonce):
// exactly 32 hex chars. Reject other shapes early so a K_epoch holder can't
// bloat the spent-set with oversized keys, and to keep the key space canonical.
if _, err := hex.DecodeString(clientR); err != nil || len(clientR) != 32 {
return nil, FingerprintData{}, 0, errors.New("invalid ticket in challenge response")
}
// Verify freshness + PoW-salt/difficulty authenticity and recover the
// per-epoch sign key (stateless). Key knowledge is proven by `sig` below.
signKey, ok := c.verifyChallenge(clientR, clientTS, clientPowSalt, clientPowMAC, clientDifficulty)
if !ok {
return nil, FingerprintData{}, 0, errors.New("invalid ticket in challenge response")
}
// Impossible difficulty is a deliberate hard-block: never accept anything.
// clientDifficulty is now MAC-authenticated, so this enforces the difficultyView on GitHub (pinned to 909b515798)
Solutions
- Issue a fresh challenge to the client when the ticket is stale
- Verify clock skew between crowdsec nodes is minimal so freshly issued tickets aren't rejected as expired
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at pkg/appsec/challenge/challenge.go:640 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06).
Data as JSON: /api/errors/da4f10afc0b9f74f.
Report an issue: GitHub.