Webhooks
Signature Verification
Verify HMAC-SHA256 webhook signatures to authenticate Cresora deliveries.
Every webhook delivery is signed with HMAC-SHA256 using your endpoint's signing secret. Verify this signature before processing any event.
Headers
| Header | Value |
|---|---|
X-Cresora-Signature | sha256=<hex-encoded-hmac> |
X-Cresora-Timestamp | Unix timestamp of delivery (seconds) |
Verification algorithm
- Build the signing payload:
timestamp.raw_body - Compute
HMAC-SHA256(payload, signing_secret) - Compare to the value in
X-Cresora-Signature(constant-time comparison) - Reject if timestamp is older than 5 minutes
import crypto from "node:crypto";
function verifyWebhook(req, secret) {
const timestamp = req.headers["x-cresora-timestamp"];
const signature = req.headers["x-cresora-signature"];
const body = req.rawBody; // raw bytes, not parsed JSON
// Reject stale deliveries (replay protection)
const ageSeconds = Math.floor(Date.now() / 1000) - Number(timestamp);
if (ageSeconds > 300) throw new Error("Webhook timestamp too old");
const payload = `${timestamp}.${body}`;
const expected = "sha256=" + crypto
.createHmac("sha256", secret)
.update(payload)
.digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
throw new Error("Webhook signature mismatch");
}
}import hmac, hashlib, time
def verify_webhook(body: bytes, headers: dict, secret: str):
timestamp = headers["X-Cresora-Timestamp"]
signature = headers["X-Cresora-Signature"]
age = int(time.time()) - int(timestamp)
if age > 300:
raise ValueError("Webhook timestamp too old")
payload = f"{timestamp}.{body.decode()}"
expected = "sha256=" + hmac.new(
secret.encode(), payload.encode(), hashlib.sha256
).hexdigest()
if not hmac.compare_digest(signature, expected):
raise ValueError("Webhook signature mismatch")🔒Security requirement
Always use constant-time comparison (timingSafeEqual / hmac.compare_digest). String equality (===) is vulnerable to timing attacks.
Storing your signing secret
Store the signing secret in your secrets manager (not in code or environment files committed to source control). Rotate it in the Partner Portal if compromised.