{"record":{"id":"f4ba44f96a1bc300","repo":"crowdsecurity/crowdsec","slug":"challenge-response-already-used","errorCode":null,"errorMessage":"challenge response already used","messagePattern":"challenge response already used","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/appsec/challenge/challenge.go","lineNumber":683,"sourceCode":"\tpowHash := sha256.Sum256([]byte(clientPowSalt + clientNonce))\n\tif !hasLeadingZeroBits(powHash[:], clientDifficulty) {\n\t\treturn nil, FingerprintData{}, 0, ErrChallengePoW\n\t}\n\n\t// Verify the submission signature sig = HMAC(s, r||ts||n||f), where the\n\t// secret s = HMAC(K_epoch, r) is never transmitted — a valid sig proves the\n\t// client derived s from the per-epoch key in the obfuscated dynamic module.\n\ts := deriveChallengeSecret(signKey, clientR)\n\n\texpectedSig := hmacSHA256Hex([]byte(s), []byte(clientR+clientTS+clientNonce+encryptedFingerprint))\n\tif !hmac.Equal([]byte(clientSig), []byte(expectedSig)) {\n\t\treturn nil, FingerprintData{}, 0, errors.New(\"invalid HMAC in challenge response\")\n\t}\n\n\t// Single-use: burn `r` (rejects replays). Done last so the spent-set only\n\t// grows on fully-valid submissions.\n\tif !c.spent.checkAndInsert(clientR, ticketAgeBackstop) {\n\t\treturn nil, FingerprintData{}, 0, errors.New(\"challenge response already used\")\n\t}\n\n\tobfKey := deriveFingerprintObfKey(s, clientR)\n\n\tfingerprint, err := deobfuscateFingerprint(obfKey, encryptedFingerprint)\n\tif err != nil {\n\t\treturn nil, FingerprintData{}, 0, fmt.Errorf(\"failed to deobfuscate fingerprint: %w\", err)\n\t}\n\n\tvar fpData FingerprintData\n\n\tif err := json.Unmarshal([]byte(fingerprint), &fpData); err != nil {\n\t\treturn nil, FingerprintData{}, 0, fmt.Errorf(\"%w: failed to unmarshal fingerprint data: %w\", ErrChallengePayload, err)\n\t}\n\n\t// Debug diagnostic: a validated submission. Guarded so `k_epoch` (forgeable\n\t// signing material — DESIGN.md §2.1) is only formatted at debug.\n\tif c.log().Logger.IsLevelEnabled(log.DebugLevel) {","sourceCodeStart":665,"sourceCodeEnd":701,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/appsec/challenge/challenge.go#L665-L701","documentation":"Replay protection failure in ValidateChallengeResponse: the nonce/ticket material in this response has already been consumed (single-use enforcement), so an identical or replayed submission is rejected.","triggerScenarios":"Thrown at pkg/appsec/challenge/challenge.go:683 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Treat as replay: deny the request and consider escalating remediation for repeated replays","Investigate caching layers (CDN, proxy) that might resubmit a previously served challenge response"],"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"}