Webhook signing
We sign each webhook call with a signature in the request header. This will allow a third party to verify that a request was sent by Host.
When a webhook is created for a client we auto-generate a secret key for each endpoint and we use that secret key to sign the request. The signature is added into the header of that request under the 'Host-Signature' attribute.
Contact Host Building to be provided with your secret key, you will need to provide the region and endpoint being called.
Example request
This is an example of a signed request with headers. Please note in this example we have indented the data for display purposes, and that were are not currently displaying exactly how the request is formatting as it will not fit in one line.
POST https://127.0.0.1/events-webhook
Connection: close
Content-Length: 481
Content-Type: application/json
Host: webhook.site
Host-Signature: t=1645157641,signature=d4e962829fd4c119171aa18cf68f430e9019c70da6c3f219a2a6dbd057146569
{
"event": "event_booked",
"event_at": "2022-02-22T06:49:37+00:00",
"source": "localhost",
"data": {
"id": "7382",
"event_id": "836",
"event_name": "STEM Workshops for Home Schooled Students",
"event_image": "https://cdn.au-staging.host-building.com/data/2020/Jul/01/6b9345c1568c8a091bc6a3b0c5dd6f62---JJC---L-https-cdnevbuccom-images-102417088-56005389539-1-original.jpg",
"user": {
"id": "46990",
"name": "Kish Jeetun",
"email": "kish.jeetun@cbre.com"
},
"session_start": "2022-02-23 10:00:00",
"session_end": "2022-02-23 11:00:00",
"cost": "$0.00"
}
}
Validating a request
Signature from the headers above:
Host-Signature: t=1645512577,signature=d4e962829fd4c119171aa18cf68f430e9019c70da6c3f219a2a6dbd057146569
The signature is made up of two components:
- a 't' component which is the timestamp when the request was signed;
- a 'signature' component which is the hashed signature.
The signature was computed using Hash Message Authentication Code (HMAC) with SHA-256 algorithm.
Step 1. Get the signature from the request
Split the two components of the signature using the "," character, then separate each component on the "=" character to get the key and value pair. The value 't' refers to the timestamp and 'signature' to the hash signature.
Step 2. Create string to compute your own signature
Concatenate the following components to a single string:
- The timestamp
- The "." character
- The JSON payload as string, after squashing extra whitespace and new lines
Referring to the above request, you should have the following string
1645512577.{"event":"event_booked","event_at":"2022-02-22T06:49:37+00:00","source":"localhost","data":{"id":"7382","event_id":"836","event_name":"STEM Workshops for Home Schooled Students","event_image":"https:\/\/cdn.au-staging.host-building.com\/data\/2020\/Jul\/01\/6b9345c1568c8a091bc6a3b0c5dd6f62---JJC---L-https-cdnevbuccom-images-102417088-56005389539-1-original.jpg","user":{"id":"46990","name":"Kish Jeetun","email":"kish.jeetun@cbre.com"},"session_start":"2022-02-23 10:00:00","session_end":"2022-02-23 11:00:00","cost":"$0.00"}}
Step 3. Compute signature
Use your webhook secret key, to compute a HMAC with SHA256 hash and compare the signature in the request header matches your computed signature.
For most languages it is not recommended to use "==" or "===" when comparing HMAC hashes, instead use your language specific function for comparing hashes.
Code Examples
- PHP
- Go
// Your Secret Key
$secretKey = 'b964e986-dc94-42e6-b24e-cb1ff2fd6fd4';
// Generate your own signature
$computedSignature = hash_hmac(
'sha256',
'1645512577.{"event":"event_booked","event_at":"2022-02-22T06:49:37+00:00","source":"localhost","data":{"id":"7382","event_id":"836","event_name":"STEM Workshops for Home Schooled Students","event_image":"https://cdn.au-staging.host-building.com/data/2020/Jul/01/6b9345c1568c8a091bc6a3b0c5dd6f62---JJC---L-https-cdnevbuccom-images-102417088-56005389539-1-original.jpg","user":{"id":"46990","name":"Kish Jeetun","email":"kish.jeetun@cbre.com"},"session_start":"2022-02-23 10:00:00","session_end":"2022-02-23 11:00:00","cost":"$0.00"}}',
$secretKey
);
// compare signature
if (hash_equals($computedSignature, 'd4e962829fd4c119171aa18cf68f430e9019c70da6c3f219a2a6dbd057146569')) {
echo "Signature is valid.";
exit;
}
echo "Signature is NOT valid";
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
)
func main() {
// define variables
secretKey := "b964e986-dc94-42e6-b24e-cb1ff2fd6fd4"
data := `{"event":"event_booked","event_at":"2022-02-22T06:49:37+00:00","source":"localhost","data":{"id":"7382","event_id":"836","event_name":"STEM Workshops for Home Schooled Students","event_image":"https://cdn.au-staging.host-building.com/data/2020/Jul/01/6b9345c1568c8a091bc6a3b0c5dd6f62---JJC---L-https-cdnevbuccom-images-102417088-56005389539-1-original.jpg","user":{"id":"46990","name":"Kish Jeetun","email":"kish.jeetun@cbre.com"},"session_start":"2022-02-23 10:00:00","session_end":"2022-02-23 11:00:00","cost":"$0.00"}}`
timeStamp := "1645512577"
signatureToCompare := "d4e962829fd4c119171aa18cf68f430e9019c70da6c3f219a2a6dbd057146569"
// set HMAC algorithm and key and get handler
hmacHandler := hmac.New(sha256.New, []byte(secretKey))
// combine timestamp with "." character and data and then write it to handler
hmacHandler.Write([]byte(timeStamp + "." + data))
// compute hash
computedHash := hex.EncodeToString(hmacHandler.Sum(nil))
// compare if hmac hashs matches
if hmac.Equal([]byte(signatureToCompare), []byte(computedHash)) {
fmt.Println("Signature is valid")
return
}
fmt.Println("Signature is NOT valid")
}