crowdsecurity/crowdsec · warning
failed to decode obfuscated fingerprint: %w
Error message
failed to decode obfuscated fingerprint: %w
What it means
deobfuscateFingerprint base64-decodes the client-supplied fingerprint blob (repeating-key XOR + base64 applied client-side) before validating the challenge response. This error means the payload is not valid standard base64, i.e. the client sent malformed or tampered fingerprint data.
Source
Thrown at pkg/appsec/challenge/ticket.go:161
return false
}
}
return true
}
// deriveFingerprintObfKey returns the keystream key for the fingerprint
// payload: `HMAC(s, "fpenc"||r)`.
func deriveFingerprintObfKey(s, r string) string {
return hmacSHA256Hex([]byte(s), []byte("fpenc"+r))
}
// deobfuscateFingerprint reverses the client-side repeating-key XOR + base64
// applied to the fingerprint JSON, using the key from deriveFingerprintObfKey.
func deobfuscateFingerprint(obfKey string, payload string) (string, error) {
payloadBytes, err := base64.StdEncoding.DecodeString(payload)
if err != nil {
return "", fmt.Errorf("failed to decode obfuscated fingerprint: %w", err)
}
out := make([]byte, len(payloadBytes))
for i := range payloadBytes {
out[i] = payloadBytes[i] ^ obfKey[i%len(obfKey)]
}
return string(out), nil
}
// verifyChallenge gates timestamp freshness and authenticates the PoW-salt
// binding, returning the per-epoch sign key (for deriving `s`) on success.
// Stateless — any instance sharing the master secret can verify. Knowledge of
// the per-epoch key is proven separately by the caller's `sig` check.
func (c *ChallengeRuntime) verifyChallenge(clientR, clientTS, clientPowSalt, clientPowMAC string, clientDifficulty int) ([]byte, bool) {
tsVal, err := strconv.ParseInt(clientTS, 10, 64)
if err != nil || tsVal <= 0 {View on GitHub (pinned to 909b515798)
Solutions
- Check that the client-side challenge script and crowdsec appsec component are the same version (obfuscation key/scheme must match)
- Log the raw payload and verify it is valid standard base64 (padding, charset)
- Rule out proxies/WAFs rewriting the request body (re-encoding, chunk mangling)
- If you wrote the client, confirm you XOR with the same key and use StdEncoding (not URL/raw encoding) before sending
Example fix
// before (client, JavaScript) const payload = btoa(xored) // after — use URL-safe-free standard base64 matching Go StdEncoding, ensure binary-safe XOR bytes const payload = btoa(String.fromCharCode(...xoredBytes))
Defensive patterns
Strategy: validation
Validate before calling
if _, err := base64.StdEncoding.DecodeString(payload); err != nil {
// reject before calling validation
} Try / catch
fp, err := deobfuscateFingerprint(obfKey, payload)
if err != nil {
log.Debugf("bad fingerprint payload: %v", err)
return challengeFailed
} Prevention
- Keep challenge client script and server in version lockstep
- Validate payloads are base64 at the edge before processing
- Test through any proxy chain that might rewrite bodies
When it happens
Trigger: ValidateChallengeResponse receives a fingerprint payload that fails base64.StdEncoding.DecodeString — the bouncer/client sent a corrupted, truncated, or non-base64 obfuscated fingerprint, or encrypted it with a different scheme than the server expects.
Common situations: Version mismatch between client-side challenge JS/bouncer and server (obfuscation scheme changed), intermediaries mangling the POST body, or a malicious/broken client submitting garbage instead of the computed fingerprint.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- invalid schema name
- ref cannot be empty
- on_challenge_submit hooks are only valid in-band, not under
- max_body_size must be a positive integer
- no zones defined
AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06).
Data as JSON: /api/errors/dfb6a16724f8c582.
Report an issue: GitHub.