Checking webhook signatures (HMACs)
When Kindly sends data to external services (e.g. when triggering a webhook to a service owned by you), the payload will be authenticated with a hash-based message authentication code (HMAC).
The key used to create the HMAC is a shared secret, and you verify it by running the algorithm yourself with the payload and the key to re-create the HMAC.
This verifies that the sender of the message knows the secret key.

Secret key

The secret key used in the algorithm depends on which kind of request is being sent, and you can find your keys in the Kindly web interface.
If you suspect that the key might have been compromised, you can revoke it and issue a new key in the web interface.

Algorithm

The HMAC is created with the HMAC_SHA256 algorithm, then encoded in base 64.
The HMAC is attached to the request in the Kindly-HMAC header. You should also read the value of the Kindly-HMAC-algorithm request header and verify that it matches HMAC-SHA-256 (base64 encoded). If it becomes necessary to change the algorithm in the future, this value will change to let you know.
1
request.headers["kindly-hmac"] = base64_encode(hmac_sha256(request.body, secret_key))
2
request.headers["kindly-hmac-algorithm"] = "HMAC-SHA-256 (base64 encoded)"
Copied!

Verifying authenticity

Verifying a HMAC-SHA256-value should be quite simple by using a crypto library in your preferred language.

JS/Node example

1
const CryptoJS = require('crypto-js');
2
3
4
function verifyHmac(
5
receivedHmac, // received in Kindly-HMAC request header
6
dataString, // received in request body (stringified JSON)
7
key // shared secret, known by your service
8
) {
9
const generatedHmac = CryptoJS.HmacSHA256(dataString, key);
10
const generatedHmacBase64 = CryptoJS.enc.Base64.stringify(generatedHmac);
11
12
return generatedHmacBase64 === receivedHmac;
13
}
14
15
// test the function
16
const receivedHmac = "uEeD0Q7eW9btdx6LFvvlpwkzQBWdbknsQkg1C27Cx7Q=";
17
const dataString = '{"foo":1,"bar":2}';
18
const key = "examplekey";
19
20
if (!verifyHmac(receivedHmac, dataString, key)) {
21
throw new Error("HMAC did not authenticate");
22
}
Copied!

Python example

The hashing algorithm works on bytes, so we make sure to do any necessary conversions from unicode str to bytes.
1
import base64
2
import hashlib
3
import hmac
4
5
6
def verify_kindly_hmac(headers: dict, request_data: bytes, secret_key: str):
7
# Note: HTTP headers are case insensitive
8
alg = headers.get('kindly-hmac-algorithm')
9
mac = headers.get('kindly-hmac')
10
11
if not (alg and mac):
12
print("HMAC was not provided")
13
return False
14
15
if alg != "HMAC-SHA-256 (base64 encoded)":
16
print("Unexpected HMAC algorithm")
17
return False
18
19
msg = request_data
20
key = secret_key.encode('utf-8')
21
22
received_hmac_b64 = mac.encode('utf-8')
23
generated_hmac = hmac.new(key=key, msg=msg, digestmod=hashlib.sha256).digest()
24
generated_hmac_b64 = base64.b64encode(generated_hmac)
25
26
match = hmac.compare_digest(received_hmac_b64, generated_hmac_b64)
27
28
if not match:
29
print('HMACs did not match: {} {}'.format(received_hmac_b64, generated_hmac_b64))
30
return False
31
32
return True
Copied!
Last modified 6mo ago