Verifying a webhook subscription

Last updated
Was this helpful?

Last updated
Was this helpful?
Was this helpful?
<?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)