crowdsecurity/crowdsec · error

ErrChallengeDifficulty

ErrChallengeDifficulty

Error message

challenge difficulty is impossible

What it means

Sentinel error ErrChallengeDifficulty: the difficulty value submitted by the client in the challenge response is >= PowDifficultyImpossible, i.e. the client claims a difficulty so high it could never have been legitimately issued by the runtime — a sign of a tampered or replayed challenge payload. Mapped to the 'difficulty' failure reason in the appsec dispatcher metrics.

Source

Thrown at pkg/appsec/challenge/challenge.go:61

	"golang.org/x/sync/singleflight"
	"golang.org/x/sys/cpu"
)

// Internal URL paths the challenge runtime intercepts. Bouncers MUST forward
// these to the WAF unmodified; they are served by the appsec dispatcher
// (pkg/appsec/appsec.go) rather than by the protected origin.
const (
	ChallengeJSPath        = "/crowdsec-internal/challenge/challenge.js"
	ChallengeSubmitPath    = "/crowdsec-internal/challenge/submit"
	ChallengePowWorkerPath = "/crowdsec-internal/challenge/pow-worker.js"
	ChallengeFPScannerPath = "/crowdsec-internal/challenge/fpscanner.js"
)

// Sentinel errors (reasons) returned by ValidateChallengeResponse.
var (
	ErrChallengeFields     = errors.New("missing required fields in challenge response")
	ErrChallengeTicket     = errors.New("invalid ticket in challenge response")
	ErrChallengeDifficulty = errors.New("challenge difficulty is impossible")
	ErrChallengePoW        = errors.New("invalid proof-of-work in challenge response")
	ErrChallengeHMAC       = errors.New("invalid HMAC in challenge response")
	ErrChallengePayload    = errors.New("invalid challenge response payload")
)

// ChallengeCookieName is the name of the sealed cookie carrying the
// successfully-validated fingerprint between requests.
const ChallengeCookieName = "__crowdsec_challenge"

// cryptoObfuscationPoolDefaultSize is how many obfuscations of the per-epoch
// key module to keep per live epoch. Each variant embeds the same key
// differently (per-visitor byte variance); default 1 keeps prior behavior.
const cryptoObfuscationPoolDefaultSize = 1

// defaultCookieTTL is the default challenge-cookie validity. Decoupled from the
// keyring window (enforced by not_after in the envelope), so cookies can
// outlive the per-epoch signing window without widening forgery exposure.
const defaultCookieTTL = 12 * time.Hour

View on GitHub (pinned to 909b515798)

Solutions

  1. Treat as a failed challenge: the request is denied or re-challenged per the appsec remediation config
  2. If false positives occur, verify the client's JS challenge module is up to date and that the difficulty in the issued ticket matches what the server validates
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at pkg/appsec/challenge/challenge.go:61 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/262bdbe777ce4b50. Report an issue: GitHub.