Signature Verification
Filoxenos signs all webhook events by including a signature in every X-Filoxenos-Signature header. This allows you to verify that the events were sent by Filoxenos and not by a third party.
Verification Steps
1
Extract the signature and timestamp
Retrieve X-Filoxenos-Signature and X-Filoxenos-Timestamp from the headers.
2
Prepare the signed payload
The signature is computed using the raw request body as a UTF-8 string.
3
Compute the HMAC-SHA256
Use your webhook secret to compute the HMAC signature of the payload.
Code Examples
Node.js
javascript
const crypto = require('crypto');
function verifyWebhook(payload, signature, timestamp, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
const received = signature.replace('sha256=', '');
// Timing-safe comparison
const valid = crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(received)
);
// Replay protection (5 minutes)
const age = Math.abs(Date.now() / 1000 - parseInt(timestamp));
if (age > 300) return false;
return valid;
}Python
python
import hmac, hashlib, time
def verify_webhook(payload: bytes, signature: str, timestamp: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
payload,
hashlib.sha256
).hexdigest()
received = signature.replace('sha256=', '')
if not hmac.compare_digest(expected, received):
return False
# Replay protection
age = abs(time.time() - int(timestamp))
return age <= 300PHP
php
function verifyWebhook($payload, $signature, $timestamp, $secret) {
$expected = hash_hmac('sha256', $payload, $secret);
$received = str_replace('sha256=', '', $signature);
if (!hash_equals($expected, $received)) return false;
// Replay protection
$age = abs(time() - intval($timestamp));
return $age <= 300;
}Never Skip Verification
Processing unverified webhooks exposes your application to spoofing and replay attacks. Always verify signatures in production environments.