Securing Webhooks

Validate that webhook requests are coming from Swivle.

Jouke Jongsma avatar
Written by Jouke Jongsma
Updated over a week ago

Dear Swivle user, we are in the process of migrating the Swivle documentation to the main WoodWing Help Center.

The article that you are reading now will therefore not be updated anymore.

For the latest information about this topic, please visit the new version of this article.

When making use of webhooks in Swivle, you will probably want to make sure that the received messages are those from Swivle only. After all, your webhook URL must be publicly accessible.

The easiest method to do this is to use the payload signature that is provided by Swivle.

Swivle generates a signature for each request by using the payload and the secret token of the webhook. The signature is computed as an HMAC SHA256 hex digest and included as the X-Hook-Signature request header.

To verify the authenticity of the payload, you can compute a hash using the webhook’s secret token that was generated by Swivle and compare it to the signature in the request.

The following is an example of how this can be achieved using NodeJS and Express.

Imagine you have configured your webhook to be using the following endpoint:

app.post('/', (request, response, next) => {
var data = [];

request.on('data', (chunk) => {
data += chunk;
});

request.on('end', () => {
console.log('Data received: ' + JSON.parse(data));
response.send(null, 200);
});

request.on('error', (error) => {
return next(err);
});
});

To add the signature validation, you can do something like:

const crypto = require('crypto');
const compare = require('secure-compare');

const SECRET_TOKEN = process.env.WEBHOOK_SECRET_TOKEN;

app.post('/', (request, response, next) => {
var data = [];

request.on('data', (chunk) => {
data += chunk;
});

request.on('end', () => {
var signature = request.header("X-Hook-Signature");
if (!isValidSignature(signature, data)) {
console.log("Signature invalid!");
response.send("Invalid webhook signature.", 400);
return;
}

console.log('Data received: ' + JSON.parse(data));
response.send(null, 200);
});

request.on('error', (error) => {
return next(err);
});
});

function isValidSignature(signature, data) {
var hmac = crypto.createHmac("sha256", SECRET_TOKEN);
hmac.update(data);
return compare(hmac.digest("hex"), signature);
}

Notes

  • Treat the secret token as a password. Do not hardcode it in your app, and do not commit it to version control.

  • The use of the === operator to compare the digest to the signature is not recommended. Instead, a package such as secure-compare provides a way to perform a "constant time" string comparison, which renders it safe from certain timing attacks against regular equality operators.

Did this answer your question?