Signature Verification

Hookdeck does not verify the incoming webhook signature by default. 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 as a replacement for verifying the original provider signature unless you are using a Hookdeck integration that supports signature verification. Hookdeck includes an X-Hookdeck-Verified header to specify the original request was verified by a Hookdeck integration. If X-Hookdeck-Verified is true that you can safely just verify the Hookdeck signature.

Proper documentation for Hookdeck integrations is coming soon. It's possible to configure verification with most platforms using our dedicated integrations or our "configure-your-own" integration that supports generic implementations of Basic Auth, API Keys, and HMAC signature.

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);
  }
});