Verifying Webhook Signatures
For the reasons explained in Consuming Webhooks, Hook0 includes a signature with each sent webhook. This signature is a cryptographically secure hash of the payload, created using a secret key that only you and Hook0 know (it is associated with the subscription that sends the webhook). By comparing the signature included in the webhook with the one you compute yourself when receiving it, you can verify the authenticity of the webhook.
How It Works
-
When we send a webhook, we generate a signature. This signature is computed by taking a hash (HMAC-SHA-256 in our case) of the event payload (and a few metadata), using a secret key that only you and Hook0 know.
-
This signature is included in a
X-Hook0-Signature
header of the webhook. -
When you receive a webhook, before processing it, you should compute the signature on your side. To do this, see the next section.
-
Then, you compare the signature you computed with the signature included with the webhook. If they match, you can be sure that the webhook is authentic and was sent by Hook0. If they don't match, you should reject the event.
-
Now that your have established that the webhook you received was sent by Hook0 and was not altered, you should also compare the timestamp included in the
X-Hook0-Signature
header with the current timestamp and reject the webhook if it is too old. This helps mitigate replay attacks. A tolerance of 5 minutes is a good default value.
Our SDKs can verify incoming webhooks for you!
You can use one of our SDKs to verify the signature of incoming webhooks so you don't have to implement the whole verification algorithm yourself.
Verifying Webhook Signatures v1 (recommended)
Here are the steps to verify webhook signatures:
Step 1: Extract the timestamp and signature from the header
First, split the X-Hook0-Signature
header value using the ,
character as the separator to get a list of elements. Then split each element using the =
character as the separator to get a key-value pair.
The t
field corresponds to the timestamp, the h
field to the list of headers included in the signature and the v1
field corresponds to the signature. You can ignore all other elements.
Step 2: Prepare the signed_payload
string
signed_payload
stringPrepare a header_values
string by:
- Splitting the
h
field using the - Replacing each header name by its value, taken from the incoming HTTP request
- Joining them (in the same order) using a
.
character as separator
Then, the signed_payload
string is created by concatenating the following:
- The timestamp (as a string)
- The character
.
- The content of the
h
field - The character
.
header_values
- The character
.
- The actual payload (that is, the request body)
Step 3: Determine the expected signature
Compute a HMAC-SHA-256 signature. Use the subscription’s signing secret as the key (you can get it from Hook's API or UI), and the signed_payload
string as the message.
Step 4: Compare the signatures
Compare the signature provided in the v1
field of the header to the signed_payload
string. If the signatures match, compute the difference between the current timestamp and the received timestamp (t
field). Decide if this difference falls within your acceptable limits.
To safeguard against timing attacks, use a constant-time string comparison to compare the computed and expected signatures.
Limitations and Considerations
-
The security of this method hinges on the secrecy of the secret key. If a malicious actor gains access to your secret key, they could create valid signatures and send fake events that would pass verification.
-
Supplement with other security measures:
- use HTTPS for your subscription's target URL to secure data transmission
- if you can, avoid transmitting personal or sensible information in the webhook's payload
About v0 and v1 signatures
We recently introduced v1 signatures in order to increase security (basically, include some other header values such as
X-Event-Id
andX-Event-Type
in the signature). As such,h
andv1
fields were introduced in the value of theX-Hook0-Signature
header. v0 signatures are still provided in that same header to ensure compatibility:
- old verification code will find
t
andv0
fields and ignore new fields; verification will continue to work- new verification code will find
t
,h
andv1
fields and perform a better verification process while thev0
is ignoredWe advice users to switch to the v1 signature verification process as it is more secure and more future-proof.
Verifying Webhook Signatures v0 (deprecated)
Here are the steps to verify webhook signatures:
Step 1: Extract the timestamp and signature from the header
First, split the X-Hook0-Signature
header value using the ,
character as the separator to get a list of elements. Then split each element using the =
character as the separator to get a key-value pair.
The t
field corresponds to the timestamp and the v0
field corresponds to the signature. You can ignore all other elements.
Step 2: Prepare the signed_payload
string
signed_payload
stringThe signed_payload
string is created by concatenating the following:
- The timestamp (as a string)
- The character
.
- The actual payload (that is, the request body)
Step 3: Determine the expected signature
Compute a HMAC-SHA-256 signature. Use the subscription’s signing secret as the key (you can get it from Hook's API or UI), and the signed_payload
string as the message.
Step 4: Compare the signatures
Compare the signature provided in the v0
field of the header to the signed_payload
string. If the signatures match, compute the difference between the current timestamp and the received timestamp (t
field). Decide if this difference falls within your acceptable limits.
To safeguard against timing attacks, use a constant-time string comparison to compare the computed and expected signatures.
Updated 23 days ago