{"record":{"id":"da4f10afc0b9f74f","repo":"crowdsecurity/crowdsec","slug":"invalid-ticket-in-challenge-response","errorCode":null,"errorMessage":"invalid ticket in challenge response","messagePattern":"invalid ticket in challenge response","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/appsec/challenge/challenge.go","lineNumber":640,"sourceCode":"\tclientNonce := vars.Get(\"n\")\n\tclientPowSalt := vars.Get(\"p\")\n\tclientPowMAC := vars.Get(\"m\")\n\tclientDifficultyStr := vars.Get(\"d\")\n\tclientPath := vars.Get(\"u\")\n\n\tif encryptedFingerprint == \"\" || clientR == \"\" || clientTS == \"\" || clientSig == \"\" || clientNonce == \"\" || clientPowSalt == \"\" || clientPowMAC == \"\" || clientDifficultyStr == \"\" || clientPath == \"\" {\n\t\treturn nil, FingerprintData{}, 0, errors.New(\"missing required fields in challenge response\")\n\t}\n\n\t// Override the request path so the WAF sees the original URL the client\n\trequest.URL.Path = clientPath\n\n\t// The difficulty the client claims it solved. It is untrusted until the PoW\n\t// MAC (which binds it) is verified in verifyChallenge below. Bound to the\n\t// valid PoW range so a malformed value can't reach the PoW/seal logic.\n\tclientDifficulty, err := strconv.Atoi(clientDifficultyStr)\n\tif err != nil || clientDifficulty < PowDifficultyDisabled || clientDifficulty > PowDifficultyImpossible {\n\t\treturn nil, FingerprintData{}, 0, errors.New(\"invalid ticket in challenge response\")\n\t}\n\n\t// Server-issued `r` is a 16-byte nonce in hex (generateChallengeNonce):\n\t// exactly 32 hex chars. Reject other shapes early so a K_epoch holder can't\n\t// bloat the spent-set with oversized keys, and to keep the key space canonical.\n\tif _, err := hex.DecodeString(clientR); err != nil || len(clientR) != 32 {\n\t\treturn nil, FingerprintData{}, 0, errors.New(\"invalid ticket in challenge response\")\n\t}\n\n\t// Verify freshness + PoW-salt/difficulty authenticity and recover the\n\t// per-epoch sign key (stateless). Key knowledge is proven by `sig` below.\n\tsignKey, ok := c.verifyChallenge(clientR, clientTS, clientPowSalt, clientPowMAC, clientDifficulty)\n\tif !ok {\n\t\treturn nil, FingerprintData{}, 0, errors.New(\"invalid ticket in challenge response\")\n\t}\n\n\t// Impossible difficulty is a deliberate hard-block: never accept anything.\n\t// clientDifficulty is now MAC-authenticated, so this enforces the difficulty","sourceCodeStart":622,"sourceCodeEnd":658,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/appsec/challenge/challenge.go#L622-L658","documentation":"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.","triggerScenarios":"Thrown at pkg/appsec/challenge/challenge.go:640 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","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"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"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"}