Skip to main content

Securing webhooks

When subscribing to the Cirrus Webhook, your application must be available on a public URL, and cannot require any HTTP authentication. This set-up means that any request arriving at your endpoint will trigger your applications event handling logic. If this is undesirable, you can verify the request signature to ensure the request originated from Cirrus.

Each webhook request originating from Cirrus contains a HMAC-SHA256 hash of the request body in the X-Hook-Signature header. This signature is calculated using the webhook secret as the key.

  1. Calculate the hash of the (raw) request body using your stored webhook secret.
  2. Verify your calculated hash matches the contents of the X-Hook-Signature request header. To prevent timing attacks, compare the hashes using a constant-time function like the timingSafeEqual node.js function.
  3. Reject the request if the signature does not match, or process the event if the signature matches.

Sample code

The following code sample shows how to validate webhook request signatures using node.js and Express.js.

import Express from "express";
import { createHmac, timingSafeEqual } from "crypto";

const app = Express()

app.post(
"/webhook",
Express.json({
verify: (req, res, bodyBuffer) => {
// TODO(developer): Retrieve your own webhook secret from your storage
// or configuration system here.
const myWebhookSecret = "dGhlc2UgYXJlIG5vdCB0aGUgYnl0ZXMgeW91IGFyZSBsb29raW5nIGZvcgo="

const key = Buffer.from(myWebhookSecret, "base64");
const hmac = createHmac("SHA256", key);
const requestSignature = hmac.update(bodyBuffer).digest("hex");
const headerSignature = req.headers["x-hook-signature"];

if (!timingSafeEqual(requestSignature, headerSignature)) {
throw new Error("Webhook signature mismatch")
}
},
}),
(req, res, next) => {
const { event, postedAt } = req.body;
// Do regular processing...

return res.sendStatus(200)
})

app.listen(8080, () => console.log(`app listening`))