# Verifying a webhook subscription

If you have created your [webhook subscription](https://docs.bird.com/api/notifications-api/api-reference/webhook-subscriptions/create-a-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:&#x20;

`messagebird-signature`&#x20;

`messagebird-request-timestamp`&#x20;

In addition, parse the request `URL` and the request `Body`.&#x20;

To calculate the request signature:&#x20;

1. Base64 decode the `messagebird-signature` header;&#x20;
2. Create a SHA256 hash checksum of the request `body` as a binary result;&#x20;
3. Join the request timestamp (`messagebird-request-timestamp` header) with the request URL and `request body` checksum computed in step 2, separated by a new line (`\n`);&#x20;
4. Calculate HMACSHA256 using the signing key as the secret and the joined payload from step 3 to calculate the signature;&#x20;
5. 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.

<figure><img src="https://3210271997-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FdnJZeZvhOMhDQA8SpjQM%2Fuploads%2FwWeMF33OfhivMuL9Kc0y%2FScreenshot%202024-11-26%20at%2010.13.04.png?alt=media&#x26;token=db48d978-413c-47e6-b940-0f46a0b5bbd2" alt=""><figcaption><p>Example of header content used for validation. You can see the messagebird-signature and the messagebird-request-timestamp, which are used to validate the request. The event reference is the messagebird-request-id, which can be used for debugging failures using Webhook subscription logs. For more information regarding logs, refer to <a data-mention href="webhook-subscription-logs">webhook-subscription-logs</a>.</p></figcaption></figure>

**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.

{% tabs %}
{% tab title="PHP" %}

```
<?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'
);
```

{% endtab %}

{% tab title="Golang" %}

```
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
}
```

{% endtab %}

{% tab title="Python" %}

```
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)
```

{% endtab %}
{% endtabs %}
