Secure your webhooks

Once you've created webhooks from the dashboard and are ready to integrate them into your system, it's crucial to prioritize security to prevent vulnerabilities in your application. Here are the steps to enhance the security of your webhooks:

1. Whitelist our IP address

The first line of defense is to whitelist our IP addresses to ensure that only authorized calls from Bridge API reach your webhook endpoints. We consistently call your callback URL from the following IP addresses:

63.32.31.5
52.215.247.62
34.249.92.209

🚧

Use the X-Forwarded-For Header

To accurately identify the source IP, make use of the X-Forwarded-For header included in the HTTP request.

2. Use the Provided Secret to Check Signatures

Webhooks Signature

Every time a webhook is created, an automatically generated secret is assigned to it. This secret plays a critical role in securing your webhook communication. It is used to sign every event sent to your webhook endpoint. Keep in mind:

  • The secret is only displayed once after the webhook's creation or after updating the current secret.
  • You can update your secret from the dashboard by editing an existing webhook. The previous secret remains valid for 24 hours after the update to allow you time to replace it in your backend.

🚧

A webhook cannot have more than 2 secrets at the same time

A webhook cannot have more than two active secret signatures simultaneously. If you update your secret twice consecutively without updating your code, the first secret becomes invalid.

Verifying Webhook Signatures

To ensure the authenticity of events received at your webhook endpoints, verify their signatures. Each event includes a BridgeApi-Signature header that contains one or more signatures, each prefixed by a scheme. The only valid live signature scheme is v1. To prevent potential downgrade attacks, disregard any schemes other than v1.

Example:

BridgeApi-Signature:
v1=E5637CDB3A54ECA10DDA9D515E588B6BECDABA414537FFC488B63474081B90DF,v1=82918263E23D1A67DD5B47E190AB62EAF9DDBB9920F48569120ED67E95286CF4

These signatures are generated using an HMAC with SHA-256 hash function. The endpoint's signing secret serves as the key, and the received event, in string format, acts as the message.

πŸ“˜

It is possible to have multiple signatures with the same scheme-secret pair

This can happen when you update a webhook's secret from the Dashboard, because the previous secret is still active for up to 24 hours. During this time, your callback URL has multiple active secrets and we generated one signature for each secret.

Step 1: Extract Signatures from the Header
Split the header using the , character as the separator to obtain a list of elements. Then, split each element using the = character as the separator to isolate a prefix and value pair. The value corresponding to the prefix v1 represents the signature(s), while other elements can be discarded.

Step 2: Determine the Expected Signature
Compute an HMAC with the SHA-256 hash function. Use the endpoint's signing secret as the key and the received event (in string format) as the message.

Step 3: Compare the Signatures
Compare the signature(s) in the header to the expected signature(s).

3. Signature verification: examples

In the following examples, SECRET represents your signature secret, and PAYLOAD is the entire body sent by the webhook. When you test these examples with the provided values:

SECRET = β€œ644b2ac3-0797-4ec6-9537-cb5c0af9caf9”

PAYLOAD = "{\"content\":{\"item_id\":1234567890,\"status\":0,\"user_uuid\":\"9a95b38f-f98b-417a-988b-9d0d584893e7\"},\"timestamp\":1611681789,\"type\":\"TEST_EVENT\"}"
or depending on the method:
PAYLOAD = "{"content":{"item_id":1234567890,"status":0,"user_uuid":"9a95b38f-f98b-417a-988b-9d0d584893e7"},"timestamp":1611681789,"type":"TEST_EVENT"}"

You should obtain the following signature: FAA8ECAC21DA6405D789C76EDB4003756398E7169DACC3FA70CF5919A81374A8

Here are example code snippets to generate this result in different programming languages:

var crypto = require('crypto');
var hash = crypto.createHmac('SHA256', SECRET).update(PAYLOAD).digest('hex');
console.log(hash.toUpperCase());
Mac hasher = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(SECRET.getBytes(), "HmacSHA256");
hasher.init(secretKey);
byte[] signedContent = hasher.doFinal(PAYLOAD.getBytes());
System.out.println(DatatypeConverter.printHexBinary(signedContent));
import hmac
import hashlib

signature = hmac.new(bytes(API_SECRET , 'utf-8'), msg = bytes(message, 'utf-8'), digestmod = hashlib.sha256).hexdigest().upper()
print(signature)

πŸ“˜

For security reasons, when we call your webhook URL, ensure that your response body remains under 10 KB in size to prevent potential issues.


What’s Next