Webhooks Verification
Overview
This tutorial is designed to walk you through the process of subscribing to webhook with webhook verification turned on.
This is an optional setup, but we would recommend enabling it for security purposes.
Note: By default, while subscribing to webhook this feature is disabled. It's recommended to enable it in order to make sure the webhook comes from Cleeng and its payload wasn't modified by any malicious 3rd party during transmission.
Verification Mechanism
Cleeng provides a way to verify webhook based on Hash-based message authentication code(HMAC) signatures. During subscribing endpoint to webhook you can choose to enable it by providing configuration of this mechanism via verification
field in the request body.
Verification
configuration is a json object and looks following:
{
"name": "HMAC",
"options": {
"hashAlg": "SHA256",
"sharedSecret": "b/ds[]7+=43cnd54-12-95[sd^faas$e"
}
}
Parameter | Description |
---|---|
name | Name of webhook verification method. Currently only supported one is HMAC . |
hashAlg | Hashing function that will be used to calculate HMAC signature. Currently only supported one is SHA256 . |
sharedSecret | A secret key shared between Cleeng and publisher used for generating HMAC signature. Allowed length is between 16 and 64 bytes inclusive. For security, we recommend the length to be at least 32 bytes. |
Value of sharedSecret is a secret between Cleeng and publisher. It should not be divulged to any 3rd party. It's publisher responsibility to store/remember its value. For security purposes we don't have any mechanism to display its value to you in case it was forgotten.
3 Steps to secure Webhooks
Step 1: Generate a random sequence of characters that will be used as sharedSecret
and store it somewhere safe. The recommended length is at least 32 bytes.
Step 2: Subscribe an endpoint to webhook topic with verification field filled in. Documentation of the relevant endpoint can be found here.
Example request body:
[
{
"url": "https://example-endpoint-with-verification.com",
"verification": {
"name": "HMAC",
"options": {
"hashAlg": "SHA256",
"sharedSecret": "b/ds[]7+=43cnd54-12-95[sd^faas$e"
}
}
}
]
Step 3: Implement HMAC verification of received signature that comes with webhook to an endpoint. More about that topic can be found further in this tutorial.
Signature Verification
Once your endpoint is subscribed with verification turned on with each webhook special header X-Webhook-Signature
will be sent that contains the webhook signature.
In order to verify received a signature on endpoint following steps must be implemented:
Calculate HMAC using your sharedSecret
, SHA256
hashing function, and raw body of the received request.
Encode obtained HMAC to base64 format - that's computed signature
Compare signature from X-Webhook-Signature
header with signature computed in the previous step. If they are the same it means that the webhook comes from Cleeng and its payload wasn't modified by any 3rd party.
Code Sample - Signature Verification
const crypto = require('crypto');
// Here goes value of sharedSecret you provided during webhook subscription step
const SHARED_SECRET = '...';
module.exports.handler = async event => {
// Header containing webhook signature
const sigHeader = event.headers['X-Webhook-Signature'];
//Raw body of received request
const rawBody = event.body;
// Computed base64 encoded signature
const sig = crypto.createHmac('sha256', SHARED_SECRET)
.update(rawBody)
.digest('base64');
if (sig !== sigHeader) {
// Webhook signature does not match.
// It means that this webhook cannot be trusted and should be discarded
return {
statusCode: 200,
body: JSON.stringify({ message: 'Webhook rejected!' }),
};
}
const webhookPayload = JSON.parse(rawBody).data;
// Webhook can be trusted, process webhook...
};
// Here goes value of sharedSecret you provided during webhook subscription step
define('SHARED_SECRET', '...');
//Raw body of received request
$rawBody = file_get_contents('php://input');
// Header containing webhook signature
$sigHeader = getallheaders()['X-Webhook-Signature'];
// Computed base64 encoded signature
$sig = base64_encode(hash_hmac('sha256', $rawBody, SHARED_SECRET, true));
// Webhook signature does not match.
// It means that this webhook cannot be trusted and should be discarded
if ($sig !== $sigHeader) {
return;
}
$webhookPayload = json_decode($rawBody, true)['data'];
// Webhook can be trusted, process webhook...
Updated almost 4 years ago