Anyone who knows your generic-webhook URL can POST whatever they want at it. The receiver has no way to tell whether the payload actually came from Augur or from a curious attacker who scraped the URL out of a Slack export.
HMAC signing fixes that. We send every payload with a header:
x-augur-signature: sha256=<hex of HMAC-SHA256(secret, raw-body)>
Each webhook channel has its own auto-generated signing secret (visible under /dashboard/channels). On your side, three lines of Node verify the request before you trust the body:
const expected = "sha256=" +
crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(header), Buffer.from(expected))) {
return res.status(401).end();
}Why timing-safe compare?
A vanilla === short-circuits on the first mismatched byte — leaking how much of the signature an attacker got right via response timing. timingSafeEqualcompares constant-time regardless of input. Cheap insurance.
Replay protection
The signature alone doesn't stop replay attacks. If an attacker captures one valid payload they can re-send it later. Pair the signature check with a timestamp inside the body (Augur sends dispatched_at) and reject anything more than 5 minutes old. Slack does this; so should you.
Full reference at /docs/webhook.