Signature Verification
Every webhook delivery includes a signature that you should verify to ensure the payload was sent by urelay and has not been tampered with.
How it works
When you create a webhook, urelay generates a unique signing secret. Each webhook delivery includes a signature computed from the request body using HMAC-SHA256 with your signing secret.
Verifying signatures
Node.js
const crypto = require('crypto');
function verifyWebhookSignature(body, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(body, 'utf-8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// In your webhook handler:
app.post('/webhook', (req, res) => {
const signature = req.headers['x-urelay-signature'];
const isValid = verifyWebhookSignature(
JSON.stringify(req.body),
signature,
process.env.URELAY_WEBHOOK_SECRET
);
if (!isValid) {
return res.status(401).send('Invalid signature');
}
// Process the webhook...
res.status(200).send('OK');
});
Python
import hmac
import hashlib
def verify_webhook(body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
# In your webhook handler:
@app.route('/webhook', methods=['POST'])
def webhook():
signature = request.headers.get('X-Urelay-Signature', '')
if not verify_webhook(request.data, signature, WEBHOOK_SECRET):
return 'Invalid signature', 401
payload = request.json
# Process the webhook...
return 'OK', 200
Security considerations
- Always verify signatures in production
- Use constant-time comparison (
timingSafeEqual/compare_digest) to prevent timing attacks - If a signature is invalid, reject the request with a
401status - Keep your signing secret confidential -- if compromised, rotate the webhook