Best Practice to Secure your WebHooks
Verification Token, Signature (HMAC), Thin Payload, Mutual TLS, Best Practices.
WebHooks is a URL prepared by the application developer (client) to receive information from the API Provider (server) without polling. For example, Github sends a POST request to the developer’s URL to inform a new pull request is opened.

Making WebHooks secure is different from making web APIs secure. It is because WebHooks is a URL that’s publicly accessible on the internet. Therefore, whenever there is a request that hits the URL, it is important to ensure that the request truly came from the expected sender. Without such verification, an attacker can fake a request sent to the WebHooks URL. Currently, there are no gold standard way to secure WebHooks, however, here are some of the common solutions deployed by both the API Provider as well as the client.
Verification Token
Verification token acts like a shared secret between the client and the server. The token is created when we register a new WebHooks. This method is used by Zoom, where every client is assigned a different verification token.

Once a verification token is set and the WebHooks is registered, every request that’s dispatched to the client’s URL will include the token. It is the client’s job to verify every token that come from requests. If they match, request is accepted. Otherwise, client may ignore the requests. This way, the client can verify that the sender of the request coming in is truly the expected server.
The advantage of this method is that it is simple to implement. However, the security is limited, as the token is sent in plain text on every request. If the token is leaked, the attacker can fake a WebHooks request and pretend to be the expected server.
Request Signing & WebHooks Signatures
Signatures is another common solution selected to secure WebHooks. The procedure and its order is as shown in the Figure:
- The server / API provider will compute the signature of the plain message that will be sent. The signature is a hash-based messsage authentication code (HMAC) of a shared secret + the plain message.
- The plain message and the signature will be sent as a packet to the client.
- The client / application, upon receiving the packet, will compute the signature using the plain message received.
- The signature computed by the client is compared to the signature sent by the server. This is to verify the authenticity of the message/request.
This method can prevent data tampering attack, where the attacker intercepts a packet and change the plain message content of the packet. In such case, it can be detected as the signature generated from the modified message would be different from the signature attached in the packet. There is also no way for the attacker to compute the signature.
However, the procedure above is still prone to the replay attack, where the attacker re-transmit the whole packet. Since the signature is valid, it won’t be detected in the comparison process. To prevent this attack, a request timestamp is attached in the message payload. If the timestamp is old, the client can ignore and reject the request. This way of attaching a timestamp is used in the Stripe API.
Mutual TLS (Transport Layer Security)
By default, whenever we connect to a URL with Transport Layer Security (TLS) Handshake Protocol, the server will send its certificate to the client & the client will verify the certificate before trusting the response.
With Mutual TLS, not only the client that will verify the server, both server and client will authenticate each other.
This method is used in B2B Settings, such as in Google’s Digital Flow and DocuSign WebHooks. The server sends client a Certificate Request, containing list of Distinguished Names of root certificates that it trusts. After that, the client will reply with its certificate and any required intermediate certificates that chain to one of the Distinguished Names listed in the Certificate Request. Then, the server will verify the client’s certificate before trusting the request.

Request Signing can be implemented in application logic, as well as at lower level. The latter case would enable the WebHooks developer to enforce security without expecting anything from the application developers.
Thin Payloads
One basic issue with WebHooks verification token and signatures is that they rely on the developers to perform and do it correctly. It is up to the application developer whether they want to follow such method or not and there is no way to ensure that they are verifying the WebHooks requests.
A better method would be to only send tiny payload to the application when an event is triggered. The application will then have to send subsequent requests to the Web API to retrieve the full event. The benefit of this approach is that regardless whether authentication is performed or not, the full event is only received by the application when they send a regular authentication request to some web API.
Summary
Regardless of which method is selected, here are some best practices to keep in mind when building WebHooks:
- Provide for developers SDKs / library / sample code to authenticate WebHooks request and reject invalid request. This is to lessen the developers effort in authenticating a request and encourage the developers to include authentication.
- In signing WebHooks, include timestamp as the field to avoid replay attack.
- Never send sensitive information through WebHooks, such as passwords. Always use authenticated API for that.
- Provide regeneration of the shared secrets that is used either for verification token or signature. Thus, if the secret is compromised, we can simply regenerate secret to ensure future requests.
Resources:
[1] Create Webhook Only App, Zoom
[2] Mutual TLS Authentication, DialogFlow.
[3] Introduction to Mutual TLS, Docusign.
[4] Replay Attack, Stripe.





