12 Jul 2024
Secure Your Webhooks: Verify Signatures with Python in Serverless Setups
Introduction
When you receive a webhook, how can you be sure it’s authentic?
Verifying the payload signature from our Webhooks API is essential, and our documentation includes a comprehensive guide for Node.js (refer here).
This post shows you how to perform the same verification using Python in a serverless environment. Special thanks to Marc and Jeroen for highlighting this use case.
Matching the hash
Once we receive a notification, there are three key components to consider:
- The hash sent by the Autodesk server, found in the x-adsk-signature header.
- The Secret Token, specified with the POST tokens endpoint.
- The payload sent by Autodesk along with the signature.
The Autodesk server used the secret token as key to compute a hash using HMAC. Our job is to use our secret token to compute a hash using the same algorithm and then compare it with the value from the x-adsk-signature header.
If they match (and your token hasn't been exposed), you can be sure that the message was sent by the Webhooks API.
The Python snippet
To compute the hash, you can use the snippet below:
import json
import hmac
import hashlib
def verify_hmac(hashstring, key, body):
byte_key = key.encode('utf-8')
# If body is a dictionary, ensure it's converted to JSON string
if isinstance(body, dict):
message = json.dumps(body, ensure_ascii=False).encode('utf-8')
else:
message = body.encode('utf-8')
hash = hmac.new(byte_key, message, hashlib.sha1)
hexdigest = "sha1hash="+hash.hexdigest()
return hmac.compare_digest(hexdigest, hashstring)
hashstring = 'sha1hash=Your hash goes here'
secret_token = 'Your Secret Token goes here'
body = 'Your payload goes here'
verify = verify_hmac(hashstring, secret_token, body);
print(verify)
If replace that with your own values and run this snippet you should receive true if your hashes match and false otherwise.
Testing it locally
To test this locally, you'll need to post a secret token, create a Webhook (choose any event), capture a sample notification sent from Webhooks API and then use the three components to test the match.
There's a set of tools that can help you testing this workflow:
- Webhooks.site for receiving and capturing a notification
- VS Code extension for creating the Webhook
- URL Decoder to decode the folder URN (in case you copy that from the ACC address bar)
- Insomnia or Postman to POST the secret token
Configuring that with AWS Lambda
After testing locally, you can use the modified snippet below to verify the signature straight from AWS Lambda:
import json
import hmac
import hashlib
import os
def lambda_handler(event, context):
hashstring = event['headers']['x-adsk-signature']
print(hashstring)
key = os.environ['KEY']
body = event['body']
isMatch = verify_hmac(hashstring, key, body)
print('Testing match...',isMatch)
return {
'statusCode': 200,
'headers': {'Content-Type': 'application/json'},
'body': body
}
def verify_hmac(hashstring, key, body):
byte_key = key.encode('utf-8')
# If body is a dictionary, ensure it's converted to JSON string
if isinstance(body, dict):
message = json.dumps(body, ensure_ascii=False).encode('utf-8')
else:
message = body.encode('utf-8')
hash = hmac.new(byte_key, message, hashlib.sha1)
hexdigest = "sha1hash="+hash.hexdigest()
print(hexdigest)
return hmac.compare_digest(hexdigest, hashstring)
We have all of that in the video below: