Providing security measures for Webhooks

Protecting your client with secrets

In the previous article, I explained how you can create Webhooks micro services using Azure Functions and how to consume them.

I have left out one crucial part to all of this, how does your consuming client know that the callback trigger really came from you? If we run the API from the previous tutorial, we can POST the following request using Postman and our API will happily consume it.

This is very dangerous as it could mean that anyone that can get hold of your callback URI is able to pretend to be the Webhooks client.

There are various ways you can protect yourself against this kind of attack, one of them is to white-list the IP address of the webhooks client. However, you may never know when this may change and it can come from multiple IP addresses if multiple instances are running.

Protecting callbacks with secrets

If you recall in the previous article, when we set up our subscriptions table, we added a column called secrets. This is some “secret” key that the configuring user can set.

What our webhooks service will do when it needs to trigger the callback, is to hash the payload that it will be sending with the configured key which will generate a signature. This signature is then added to the header X-Hub-Signature which the client can then do similar steps when they receive our request and then do a comparison of the signature they calculated with the signature we send through.

Let’s get the code out which will make it clearer. In our af-webhooks-service, let’s add signature generation to our callback request.

Our getSignature method takes the payload and the key. The key we are already storing and retrieving in our Azure Storage Table (subscriptions). We are using the HmacSha1 hashing algorthim from the crypto package which allows us to hash our body content using our key.

Now we need to do similar steps on our client side code.

Here, we are doing similar signature generation for the payload using our secret.

We then use the crypto.timingSafeEqual to equate the two signatures. If we have a match, we can be sure that the request came from the webhooks client.

Note: to truly be confident that the request has come from the webhooks client, you will need to ensure that the key/secret you have chosen is complex enough for it hard to guess and that it is stored appropriately (totally not in the source file or the config for that matter). Read more on crypto.timingSafeEqual and how to further protect your client secret.

Other than that, you should be good to go. Now you can run the same test we did with Postman at the beginning of this article and you should get a bad request this time around.

Here is the full source to this solution:

Resources:

Happy coding!