Verify webhook signatures to ensure that the received events are coming from DEUNA.
Webhook events include a signature in the header called X-Deuna-Signature
.
Use the signature to verify the authenticity of the events
Protect your server
Protect your server from unauthorized webhook events.
DEUNA recommends using Hash-based Message Authentication Code (HMAC using the SHA256 algorithm) signatures for our webhooks.
For webhooks that support enabling HMAC signatures, each event includes a signature computed using a secret HMAC key and the webhook payload.
By verifying this signature, you confirm that:
- The webhook was sent by DEUNA.
- The payload was not modified during transmission.
Verify signatures
Use the raw body of the request to validate the signature. Make sure your framework doesn't manipulate the raw body.
Any modification to the raw body will cause validation to fail.
To verify the webhook signature:
- Recover the signature. Extract the header X-Deuna-Signature of the incoming webhook request.
- Generate the expected signature. Uses the same secret HMAC key (private API key) and webhook payload to generate the expected signature.
- Compare the generated signature with the received signature. If they match, then the webhook is legitimate. If they do not match, then the webhook is illegitimate.
- Check the time elapsed in the value
signed_at
. Compare the time elapsed since the signature was generated until the current moment and perform the corresponding validation.
Examples
Compare examples in multiple languages to verify SHA256 HMAC signatures.
import hmac
import hashlib
import base64
import json
from django.http import HttpResponse
def validate_hmac_sha256_signature(secret, payload, signature):
secret_bytes = bytes(secret, 'utf-8')
payload_bytes = bytes(payload, 'utf-8')
h = hmac.new(secret_bytes, payload_bytes, hashlib.sha256)
expected_signature = base64.b64encode(h.digest()).decode('utf-8')
return hmac.compare_digest(signature, expected_signature)
# Example
secret = "<your-private-api-key>"
# Using Django
@csrf_exempt
def my_webhook_view(request):
payload = request.body
sig_header = request.META['X-Deuna-Signature']
if not validate_hmac_sha256_signature(secret, payload, sig_header):
print("Invalid signature")
return HttpResponse(status=403)
return HttpResponse(status=201)
const express = require('express');
const app = express();
const crypto = require('crypto');
const secret = "<your-private-api-key>"
function verifySignature(secret, signature, payload) {
const hmac = crypto.createHmac('sha256', secret);
const computedSignature = hmac.update(payload, 'utf8').digest('base64');
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(computedSignature));
}
// Match the raw body to content type application/json
app.post('/webhook', express.raw({ type: 'application/json' }), (request, response) => {
const sig = request.headers['x-deuna-signature'];
const event = request.body;
try {
const verified = verifySignature(secret, sig, event);
console.log('Verified:', verified);
} catch (err) {
console.error(err);
response.status(400).send(`Webhook Error: ${err.message}`);
}
response.json({ received: true });
});
app.listen(4242, () => console.log('Running on port 4242'));
}