Secure your webhooks
Once you've set up your server and exposed it to the internet, it will receive any payloads that are sent to it. To improve security, you should:
- Limit requests to your server to those coming from Mambo servers
- Verify the signature sent with each request from Mambo
The signature is created using the secret key specified when you set up your webhook. Make sure to use a secure key with high entropy. To verify the signature sent with each request, the key will also need to be available on your server. Don't hardcode the key into your application; instead, use best practices for key management.
Preventing replay attacks
To mitigate the risk of replay attacks, a timestamp is included in the X-Mambo-Signature header. The timestamp is part of the signature, which means an attacker cannot change the timestamp without invalidating the signature. If the signature is valid but the timestamp is too old, your application can reject the request.
Note that each time Mambo attempts a webhook delivery, a new timestamp and signature is generated. So any time a delivery attempt fails and is retried or a manual retry is attempted, we will generate a new timestamp and signature.
Ensure your server's clock is accurate and uses Network Time Protocol (NTP) so that it aligns with Mambo's servers.
Validating the request signature
Each webhook request sent to your servers will contain the X-Mambo-Signature header. This header contains a timestamp and a hash signature (HMAC SHA-256) of the timestamp concatenated with the payload created using the secret key.
To verify the signature, you will calculate the hash locally using your secret key and compare it to the hash signature sent in the webhook request's header. The hash signature is computed using the HMAC SHA256 hex digest algorithm and applying it to the timestamp concatenated with the payload. Below is an example of the X-Mambo-Signature header:
The steps below explain how to validate the signature:
-
Extract the timestamp and signature from the header: split the header using the , (comma) character to get a list containing the timestamp and the signature. Then split each element in the list using the = (equals) character to get a key/value pair. The value for the key t is the timestamp and the value for the key v1 is the signature.
-
Prepare the payload string for hashing: create the string that will be given to the hashing function by concatenating the timestamp retrieved in the header and the payload (the request body). Simply join the two strings together (i.e. timestamp + payload).
-
Compute the signature: create an HMAC SHA256 hash. Use the string we prepared in the previous step together with the webhook's signing secret.
-
Compare the signatures: compare the signature in the header with the signature computed in the previous step. To prevent timing attacks, use a constant-time string comparison to compare the two signatures.
-
Verify the timestamp: once the signatures are validated, you can verify the timestamp in the header to check if it is within your time tolerance.
The code sample below illustrates how this can be done in Java:
There are a number of important factors to consider when implementing this code in your preferred language:
- Use a constant time string comparison to verify the signature in order to avoid timing attacks. Most languages today provide string comparison methods which use constant time comparison. Note that the MessageDigest class in Java was vulnerable to timing attacks prior to version SE 6u17.
- Make sure you handle the payload using UTF-8 character encoding as it may contain unicode characters.