api authentication ai_generated true

Webhook signature verification fails despite correct secret key

ID: api/webhook-hmac-raw-body-mismatch

Also available as: JSON · Markdown
92%Fix Rate
90%Confidence
3Evidence
2023-01-01First Seen

Version Compatibility

VersionStatusIntroducedDeprecatedNotes
any active

Root Cause

Webhook HMAC signatures are computed over raw request body bytes. JSON.stringify(parsed) != original bytes due to key ordering, whitespace, and Unicode escaping differences.

generic

Workarounds

  1. 95% success Capture raw body before JSON parsing in Express
    app.use('/webhook', express.raw({type: 'application/json'})); // req.body is Buffer
  2. 92% success In Flask, use request.get_data() not request.json
    raw_body = request.get_data(); sig = hmac.new(secret, raw_body, hashlib.sha256).hexdigest()
  3. 90% success Use timing-safe comparison for HMAC verification
    crypto.timingSafeEqual(Buffer.from(computed), Buffer.from(received))

Dead Ends

Common approaches that don't work:

  1. Compute HMAC over JSON.stringify(req.body) 95% fail

    JSON.stringify produces different bytes than the original payload. Signature will never match.

  2. Use bodyParser.json() before webhook route in Express 90% fail

    bodyParser consumes the raw body stream. The original bytes are gone by handler time.