Kintaba sends a signature with every Webhook request that you can optionally use to validate requests came from Kintaba.
We send webhook signatures under the
X-KINTABA-SIGNATURE header, and they look like this:
The example above shows the signature on two lines, but note that the actual signature comes on one line with no whitespace.
Kintaba generates signatures using HMAC with SHA256. You'll need some utility for generating proper HMACs.
Extract the timestamp and the signature from the response header by splitting on the
,, then for each respective field, split on
= to extract the value from each key-value pair.
The value for
t is the timestamp, and the value for
v1 is the signature.
signed_payload string by concatenating:
- The timestamp (the value under
tas extracted in Step 1)
- The literal character
- The request body (i.e. the JSON payload)
You'll need the webhook URL's secret key to validate the signature. To do so, navigate to the automation with the webhook in question, and click the icon to open the webhook's configuration modal:
Inside the modal, you can click the eye button to view the secret, copy this secret:
Using the signing key extracted in step 3 and the payload generated in step 2, generate an HMAC with SHA-256 with the signing key as the secret and the payload as the message.
Compare the value under the
v1 field in step 2 with the signature just generated. They should match.
Additionally, to prevent replay attacks, where an attacker captures a valid webhook request and replays it against your webhook URL over and over again, you should check the timestamp extracted in step 2 and ensure it isn't too far in the past (5 minutes is a good target).
If everything matches, congratulations, you've validated your webhook!
#!/usr/bin/env python3 # Verifies signatures for Kintaba webhooks. # note: the signature lives in the 'x-kintaba-signature' header. import hmac from hashlib import sha256 def _get_timestamp_and_sig(header): items = [i.split("=", 2) for i in header.split(",")] timestamp = int([i for i in items if i == "t"]) signature = [i for i in items if i == "v1"] return timestamp, signature def _create_sig(payload, secret): mac = hmac.new( secret.encode("utf-8"), msg=payload.encode("utf-8"), digestmod=sha256, ) return mac.hexdigest() def verify_payload(payload, header, secret, tolerance_in_secs=None): timestamp, signature = _get_timestamp_and_sig(header) if not signature: return false signed_payload = "%d.%s" % (timestamp, payload) computed_sig = _create_sig(signed_payload, secret) if computed_sig != signature: return False if tolerance_in_secs and timestamp < time.time() - tolerance_in_secs: return False return True
Updated about 1 year ago