Signature Verification

Hookdeck does not verify the incoming webhook signature. You should perform the verification yourself. Each provider has their own approach to sign their request and the correct approach can be found in their documentation. Signature verification is critical to ensure bad actors are not causing side effects in your systems by sending forged requests to your webhook endpoints. Hookdeck only appends headers to the request headers. The original headers from the provider remain available to perform the verification.

Hookdeck also resigns each request. You can verify that signature to confirm that Hookdeck sent the request to your server. That should NOT be used a replacement for verifying the original provider signature unless you are using a Hookdeck integration that supports signature verification.

In the future, we plan for Hookdeck to help you with verification by automatically verifying the requests. We are currently experimenting with Stripe webhooks. More will follow, along with proper documentation.

Verifying the Hookdeck Signature

Webhooks sent by Hookdeck are verified by calculating a digital signature. Each webhook request includes a X-Hookdeck-Signature header, which is generated using the workspace's secret along with the data sent in the request. To verify that the request came from Hookdeck, compute the HMAC digest according to the following algorithm and compare it to the value in the X-Hookdeck-Signature header. If they match, then you can be sure that the webhook was sent from Hookdeck.

Hookdeck signature uses SHA-256 algorithm and is base64 encoded.

app.use(
  express.json({
    // Store the rawBody buffer on the request
    verify: (req: any, res, buf) => {
      req.rawBody = buf;
    },
  })
);

app.post('/webhook', async (req, res) => {
  //Extract X-Hookdeck-Signature Header from the request
  const hmacHeader = req.get('X-Hookdeck-Signature');

  //Create a hash based on the parsed body
  const hash = crypto.createHmac('sha256', secret).update(req.rawBody).digest('base64');

  // Compare the created hash with the value of the X-Hookdeck-Signature header
  if (hash === hmacHeader) {
    console.log('Webhook is originating from Hookdeck');
    res.sendStatus(200);
  } else {
    console.log('Signature is invalid, rejected');
    res.sendStatus(403);
  }
});