Recipe

Verify a webhook signature

Never trust an unverified webhook. Recompute the signature over the raw request body with your endpoint secret and compare in constant time; reject on mismatch before you do anything.

2 min read

Raw bodySign over bytes
Constant-timeCompare
Reject firstThen act

Use the raw body

Signatures are computed over the exact bytes Credicorp sent. Capture the raw request body before any JSON parsing or framework middleware reformats it — a re-serialised body will not match. Read the signature from the delivery header (see webhooks).

Recompute and compare

import hmac, hashlib
expected = hmac.new(SECRET, raw_body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, header_sig):
    return Response(status=400)   # reject — do not process

Use a constant-time comparison (hmac.compare_digest) so an attacker cannot learn the signature byte by byte from timing.

Then process idempotently

Only after verification do you act — and because delivery is at-least-once, de-duplicate on the event ID so a repeat delivery is a no-op. See building an idempotent consumer. Return a 2xx once the event is safely enqueued.

Frequently asked questions

Why must I use the raw body?

The signature is over the exact bytes sent. If your framework parses and re-serialises JSON before you compute the signature, the bytes differ and verification fails. Capture the raw body first.

Why constant-time comparison?

A normal string compare returns early on the first mismatched byte, leaking timing information an attacker can use to reconstruct the signature. Constant-time comparison removes that side channel.

Funding for UK limited companies

Credicorp lends to your company, not to you personally — short-term working capital with no personal guarantee. See what your business could access.