Webhook Verification
Verify the authenticity and integrity of DebugBundle webhook deliveries using HMAC-SHA256 signatures.
Every webhook delivery from DebugBundle includes an HMAC-SHA256 signature so you can verify that the payload is authentic and hasn't been tampered with.
Signature Header
Each delivery includes the signature in the X-DebugBundle-Signature header:
X-DebugBundle-Signature: sha256=a1b2c3d4e5f6...The value is the hex-encoded HMAC-SHA256 digest of the raw request body, computed using your webhook's signing secret as the key.
Verification Algorithm
- Read the raw request body as a UTF-8 string (before any JSON parsing).
- Compute the HMAC-SHA256 of the body using your webhook signing secret.
- Compare the computed digest with the value in
X-DebugBundle-Signature(minus thesha256=prefix). - Use constant-time comparison to prevent timing attacks.
Implementation Examples
Node.js
import { createHmac, timingSafeEqual } from "node:crypto";
function verifyWebhookSignature(rawBody, signatureHeader, secret) {
const expected = createHmac("sha256", secret)
.update(rawBody, "utf8")
.digest("hex");
const received = signatureHeader.replace("sha256=", "");
if (expected.length !== received.length) {
return false;
}
return timingSafeEqual(
Buffer.from(expected, "hex"),
Buffer.from(received, "hex")
);
}Python
import hashlib
import hmac
def verify_webhook_signature(raw_body: bytes, signature_header: str, secret: str) -> bool:
expected = hmac.new(
secret.encode("utf-8"),
raw_body,
hashlib.sha256
).hexdigest()
received = signature_header.removeprefix("sha256=")
return hmac.compare_digest(expected, received)PHP
function verifyWebhookSignature(
string $rawBody,
string $signatureHeader,
string $secret
): bool {
$expected = hash_hmac('sha256', $rawBody, $secret);
$received = str_replace('sha256=', '', $signatureHeader);
return hash_equals($expected, $received);
}Express.js Middleware
A complete middleware that verifies webhook signatures before processing:
import { createHmac, timingSafeEqual } from "node:crypto";
import express from "express";
const WEBHOOK_SECRET = process.env.DEBUGBUNDLE_WEBHOOK_SECRET;
function webhookVerification(req, res, next) {
const signature = req.headers["x-debugbundle-signature"];
if (!signature) {
return res.status(401).json({ error: "missing_signature" });
}
const expected = createHmac("sha256", WEBHOOK_SECRET)
.update(req.body, "utf8")
.digest("hex");
const received = signature.replace("sha256=", "");
if (
expected.length !== received.length ||
!timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(received, "hex"))
) {
return res.status(401).json({ error: "invalid_signature" });
}
next();
}
const app = express();
// Use raw body for signature verification
app.post(
"/webhooks/debugbundle",
express.raw({ type: "application/json" }),
webhookVerification,
(req, res) => {
const event = JSON.parse(req.body.toString());
console.log("Verified webhook event:", event.event || event.event_type);
res.status(200).json({ received: true });
}
);Security Best Practices
- Always verify signatures in production. Never skip verification.
- Use raw request body for computing the signature — parse JSON only after verification.
- Use constant-time comparison (
timingSafeEqual,hmac.compare_digest,hash_equals) to prevent timing side-channel attacks. - Store secrets securely — use environment variables or a secret manager, never hard-code.
- Rotate secrets periodically — create a new webhook with a new secret, verify it works, then delete the old webhook.
- Reject unsigned requests — return 401 if the signature header is missing.
- Log verification failures — monitor for unexpected failures that could indicate misconfiguration or attack attempts.
Testing Webhooks
Use the webhook test endpoint to send a test delivery:
# CLI
debugbundle webhooks test --webhook-id wh_01H...
# API
curl -X POST https://api.debugbundle.com/v1/webhooks/wh_01H.../test \
-H "Authorization: Bearer dbundle_member_a1b2c3d4..."Test deliveries use the same signing mechanism as real deliveries — your verification code will work identically for both.
Next Steps
- Webhook Events — Event types and payload schemas
- Webhooks API — Manage webhook endpoints
- Security — DebugBundle security overview