12 Jul 2024

Secure Your Webhooks: Verify Signatures with Python in Serverless Setups

Default blog image

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:

  1. The hash sent by the Autodesk server, found in the x-adsk-signature header.
  2. The Secret Token, specified with the POST tokens endpoint.
  3. 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.

process diagram

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:

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:

 

Related Article