Security policy of webhooks
Kim Giaoui avatar
Written by Kim Giaoui
Updated over a week ago

✍️ Header signature

We sign the webhook events by including a signature in each event’s Tomorro_Signature header. Each webhook has a secret autogenerated and shared between us and the end user.

Then, to sign the event, we compute an HMAC-SHA256 of the timestamp and the event’s body:

sha256 = HMAC_SHA256(timestamp + “.” + eventBody)

Finally, the Tomorro_Signature header has the following format:

Tomorro-Signature: t=1492774577, sha256=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd

with t the timestamp and sha256 the previously computed hash.

Note that newlines have been added for clarity, but a real Tomorro_Signature header is on a single line.

🔍 Signature check

When a client receive a webhook event, he should check that the signature is valid. Here is an example of how to do it:

import {createHmac, timingSageEqual} from 'crypto'; 
function checkSignature(signature, secret, body) {
const timestamp = signature[0].split('=')[1];
const sig = Buffer.from(signature[1].split('=')[1], 'utf8');
const hmac = createHmac('sha256', secret);
const digest = Buffer.from(hmac.update(timestamp + '.' + JSON.stringify(body)).digest('hex'), 'utf8');
if (sig.length !== digest.length || !timingSafeEqual(digest, sig))
{ return false; }
else { return true; }

Note that it’s recommended to use a time constant-time string comparison to avoid timing attack vulnerability, that’s why we are using here timingSafeEqual instead of === (see

Moreover, the client should check if the difference between the received timestamp et the current timestamp is within his tolerance (this is to avoid replay attack).

Did this answer your question?