Request validation
Webhooks requests should be validated using the X-Webhook-Timestamp
and X-Webhook-Signature
headers and a shared secret key.
To verify the signature, create an SHA-256 hash of the request body with the X-Webhook-Timestamp
header value appended to it using the shared secret key for the webhook and convert it to base64. If the calculated hash matches the value of the X-Webhook-Signature
the request can be assumed to be valid.
NodeJS example
// Create digest with payload + hmac secret
const timestamp = request.get('X-Webhook-Timestamp')
const hashPayload = request.rawBody + timestamp
const hmac = crypto.createHmac('sha256', process.env.WEBHOOK_SECRET)
const digest = hmac.update(hashPayload).digest('base64')
// Get hash sent by the provider
const providerSig = request.get('X-Webhook-Signature')
// Compare digest signature with signature sent by provider
if (providerSig.length == digest.length && crypto.timingSafeEqual(digest, providerSig)) {
// Webhook Authenticated!
console.log(request.json())
}
PHP example
define('API_SECRET_KEY', 'my_webhook_api_secret');
function verify_webhook($data, $hmac_header)
{
# Calculate HMAC
$calculated_hmac = base64_encode(hash_hmac('sha256', $data, API_SECRET_KEY, true));
return hash_equals($hmac_header, $calculated_hmac);
}
# Extract the signature header
$hmac_header = $_SERVER['X-Webhook-Signature'];
# Combine the timestamp header value and raw body
$timestamp = $_SERVER['X-Webhook-Timestamp'];
$data = file_get_contents('php://input') + $timestamp;
# Compare HMACs
$verified = verify_webhook($data, $hmac_header);
if ($verified) {
# Do something with the webhook data!
}