crowdsecurity/crowdsec · error

ErrChallengeHMAC

ErrChallengeHMAC

Error message

invalid HMAC in challenge response

What it means

Sentinel error ErrChallengeHMAC: the submission signature sig = HMAC(s, r||ts||n||f) does not match the server-computed value, where the secret s is derived per-client and never transmitted. A failed HMAC means the response was not produced by a client holding the legitimate per-epoch secret — tampering, wrong key epoch, or corruption. Mapped to the 'hmac' failure reason in appsec metrics.

Source

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

)

// 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

// DefaultChallengeCSP is the Content-Security-Policy header used on the

View on GitHub (pinned to 909b515798)

Solutions

  1. Deny or re-challenge the request; treat repeated HMAC failures from one IP as suspicious
  2. Verify all crowdsec instances behind the same bouncer share the same appsec key/epoch so signatures validate consistently
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at pkg/appsec/challenge/challenge.go:63 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/5a3578c2d1b788ca. Report an issue: GitHub.