Skip to main content

Securing webhooks

When a webhook is created, a secret token is generated for you to validate messages coming from the Amani system. This secret token will be passed in request headers.

HMAC (Hash-Based Message Authentication Code) is an authentication technique used for verifying the integrity and authenticity of messages.

HMAC takes a message and a secret key as an input and uses a cryptographic hash function (such as SHA-256) to create a fixed-length string. This code is sent along with the message to the receiver. The receiver must perform the same calculations once they receive the message and match the signatures. If the input changes even slightly or a different secret key is used, the output will also change completely. This allows data tampering to be easily detected.

After a webhook endpoint is registered, Amani will generate the signature using the webhook payload, secret token and SHA256 hash function. After the signature is generated, the base64 encoded signature will be passed in the header as "Webhook-Signature":

{"Webhook-Signature": <signature>}

Calculating the signature

Here's a Python example demonstrating how to use HMAC to authenticate a request body:
import json
import hmac
import hashlib
import base64


def calculate_signature(secret, payload):
"""
Generate HMAC signature using SHA-256 hash function
:param secret: webhook secret token for signing
:param payload: webhook payload
:return: calculated signature
"""
data = json.dumps(payload).encode("utf-8")
hashed_payload = hmac.new(
key=secret.encode("utf-8"),
msg=data,
digestmod=hashlib.sha256
).digest()
signature = base64.b64encode(hashed_payload).decode()
return signature


def verify_signature(payload, webhook_signature, secret_token) -> bool:
"""
Generate HMAC signature from received data and compare the calculated signature with the received signature
:param payload: request body
:param webhook_signature: signature in the header
:param secret_token: webhook secret token for signing
:return True/False if calculated signature matches with the signature header or not
"""
calculated_signature = calculate_signature(secret=secret_token, payload=payload)
if webhook_signature != calculated_signature:
return False
return True