Webhook signatures for fun and profit

Webhooks - less painful than playing hooky by skipping work.

Image source: Encyclopedia SpongeBobia

What's a webhook?

Application programming interfaces (API) consist of client requests and server responses. Webhooks are the reverse of APIs! A third-party service (e.g. server) will send data to one or more configured listeners (e.g. clients). You can set up a listener to consume webhook events by following these steps:
  1. create a new URL in your web application to listen for events (e.g. mycoolapp.com/webhooks)
  2. create a secret token with your third-party service (e.g. GitHub repository settings)
  3. give your application access to this secret token (e.g. environment variables)
  4. deploy the application to listen for requests
  5. verify the webhook signature found in each request
  6. if the signature passes this verification step, process the event data
  7. if it doesn't pass, raise an error
Webhooks allow us to get information in real-time. Let's say we want to find out if a task has finished. Instead of polling an API and asking for the state of a task, webhooks automatically notify us when a task is done. All we have to do is verify the webhook signature.

Companies like Stripe and Twilio provide developers with software development kits (SDKs). These SDKs typically verify signatures for you. If not, have no fear! We can manually verify these signatures using Python.

Note: the terms "third-party" and "authorized users" will be used interchangeably from here on out.


Let's assume our application was partly compromised. Our webhook URL is now public and out in the open. How do we differentiate authorized users from bad actors? Our application and the third-party service need some way to authenticate messages. One way to achieve this is to use a hash-based message authentication code (HMAC).

First, an authorized user sends a signature with every request to our application. Next, our application computes the expected signature by combining HMAC with our secret token. It compares both signatures and allows requests from this user if the signatures match. Bad actors would have a hard time trying to fool us without this secret token.

Now that we've covered secret tokens, let's take a look at the code to manually verify signatures.

...but verify

We define a request "object" on line 20. We use this object to represent a request that would normally be sent by an authorized user. This request has a signature, which is a bytes string. Let's assume the signature in the request is valid. The goal of our application is to calculate this signature using HMAC and our secret token.

The shared secret is hardcoded on line 7 for demonstration purposes. Remember, the secret should be stored as an environment variable on your server!

Next, we use the hmac and the hashlib Python modules to create a hashing object on line 9.

The method signature for the new() method is: hmac.new(key, msg=None, digestmod=''):
  • key is set to the secret token encoded in bytes
  • msg is set to the request body encoded in bytes
  • digestmod is set to the SHA-1 hashing algorithm

We get the expected signature on line 14 by encoding the digest of our hashing object using Base64. You might be able to skip this step. You should confirm if the data you receive is encoded using Base64.

On line 16 we compare the signature found in the request with the signature we expect. You typically use the == operator when comparing values in Python. Do not do this here! Heed the following warning found in the Python documentation:
Warning: When comparing the output of digest() to an externally-supplied digest during a verification routine, it is recommended to use the compare_digest() function instead of the == operator to reduce the vulnerability to timing attacks.
On line 25 we combine all of this together and verify the request. We display a thumbs up emoji for authorized users and a red light emoji for bad actors!

Wrapping up

I took a cybersecurity course my last semester in college. I'd be lying if I told you I enjoyed writing C code and setting up Ubuntu virtual machines on my Windows laptop. That being said, it's awesome seeing the theories I learned in school put to practice.

Check out these links with more information on HMAC and webhook security: