Verifying a webhook subscription
If you have created your webhook subscription using a signingKey you can validate the authenticity of the webhook by validating the request signature sent in the request header.
From the incoming webhook, parse the following request headers:
messagebird-signature
messagebird-request-timestamp
In addition, parse the request URL and the request Body.
To calculate the request signature:
Base64 decode the
messagebird-signatureheader;Create a SHA256 hash checksum of the request
bodyas a binary result;Join the request timestamp (
messagebird-request-timestampheader) with the request URL andrequest bodychecksum computed in step 2, separated by a new line (\n);Calculate HMACSHA256 using the signing key as the secret and the joined payload from step 3 to calculate the signature;
Compare the output of Step 4 with the signature from Step 1. The code snippets below illustrate the intended process. We recommend tailoring these examples to fit your preferred programming language, codebase, or framework.

Examples
The following code snippets are provided to illustrate the intended process. We recommend adapting them to your preferred programming language, codebase, or framework to ensure compatibility.
<?php
function verifyWebhookSignature(
string $headerSignature,
string $headerTimestamp,
string $receivedBody,
string $requestedUrl,
string $signingKey
): bool {
$receivedDecodedSignature = base64_decode($headerSignature);
$bodyHash = hash('sha256', $receivedBody, true);
$computedSignature = hash_hmac(
'sha256',
sprintf("%s\n%s\n%s", $headerTimestamp, $requestedUrl, $bodyHash),
$signingKey,
true
);
return hash_equals($computedSignature, $receivedDecodedSignature);
}
// Example usage
$isVerified = verifyWebhookSignature(
$_SERVER['HTTP_MESSAGEBIRD_SIGNATURE'],
$_SERVER['HTTP_MESSAGEBIRD_REQUEST_TIMESTAMP'],
file_get_contents('php://input'),
'https://domain.com/webhook/bird',
'secureSigningKey'
);package main
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"fmt"
"os"
)
func main() {
var key string
var signatureHeader string
var timestampHeader string
var url string
var body []byte
// Example usage
match, err := checkSignature(key, signatureHeader, timestampHeader, url, body)
if err != nil {
// Error out
fmt.Printf("something went wrong: %v", err)
os.Exit(1)
}
if !match {
// Signature doesn't match
fmt.Println("Signatures don't match!")
} else {
// Signature matches
fmt.Println("Signatures match!")
}
}
// checkSignature verifies the Bird style HMAC-SHA256 signature for the given data.
func checkSignature(signingKey, signatureHeader, timestampHeader, requestURL string, data []byte) (bool, error) {
// Step 1: Decode the signature from the header
actualSignature, err := base64.StdEncoding.DecodeString(signatureHeader)
if err != nil {
return false, err
}
// Step 2 - 4: Calculate the expected signature
expectedSignature, err := signSha256(signingKey, timestampHeader, requestURL, data)
if err != nil {
return false, err
}
// Step 5: Compare the expected signature with the actual signature
return hmac.Equal(expectedSignature, actualSignature), nil
}
// signSha256 returns the Bird style HMAC-SHA1 signature for the given data.
func signSha256(signingKey, timestamp, requestURL string, data []byte) ([]byte, error) {
// Step 2: Calculate the SHA256 hash for the given data
bh := sha256.Sum256(data)
// Step 3: Concatenate the timestamp, request URL, and the SHA256 hash, separated by a newline character
var m bytes.Buffer
_, err := fmt.Fprintf(&m, "%s\n%s\n%s", timestamp, requestURL, bh)
if err != nil {
return []byte{}, err
}
// Step 4: Calculate the HMAC-SHA256 hash for the concatenated string using the signing key
mac := hmac.New(sha256.New, []byte(signingKey))
if _, err := mac.Write(m.Bytes()); err != nil {
return []byte{}, err
}
return mac.Sum(nil), nil
}import hashlib
import hmac
import base64
import time
class SignedRequest:
def __init__(self, requestSignature, requestTimestamp, requestBody, requestUrl):
self._requestSignature = requestSignature
self._requestTimestamp = str(requestTimestamp)
self._requestBody = requestBody
self._requestUrl = requestUrl
def verify(self, signing_key):
payload = self._build_payload()
expected_signature = base64.b64decode(self._requestSignature)
calculated_signature = hmac.new(signing_key.encode('latin-1'), payload.encode('latin-1'),
hashlib.sha256).digest()
return expected_signature == calculated_signature
def is_recent(self, offset=10):
return int(time.time()) - int(self._requestTimestamp) < offset
def _build_payload(self):
checksum_body = hashlib.sha256(self._requestBody.encode('latin-1')).digest()
str_checksum_body = checksum_body.decode('latin-1')
parts = [self._requestTimestamp, self._requestUrl, str_checksum_body]
return "\n".join(parts)Last updated
Was this helpful?

