How to Secure Webhook Endpoints
This guide will walk you through the process of securing your webhook endpoints. To get started you will need the following, both of which can be set up by following both the How to Set Up Webhooks Guide and How to Test Webhook Endpoints Guide:
- a live webhook endpoint that is receiving event data
- a subscription to at least one webhook event in the Atomic Console pointing to your live webhook endpoint
After completing this guide, you will have a webhook endpoint up and running that is securely receiving data from Atomic and rejecting content that is not sent by Atomic.
HMAC signature validation
To secure your webhook endpoint, you need to set up HMAC signature validation. When Atomic sends a webhook event, we include an X-HMAC-Signature-Sha-256
header with the request. This header is an HMAC-SHA256 hash of the request body with one of your API secrets used as the key. By validating the HMAC signature with the correct API secret, you can ensure the request came from Atomic and hasn't been tampered with.
The process of validating the HMAC signature is relatively straightforward, though implementation details will differ slightly depending on the method you used to build your webhook endpoint.
- Re-create the HMAC signature using the request body and the correct API secret.
- Compare the HMAC signature you created with the one included in the
X-HMAC-Signature-Sha-256
header. If the values match, proceed to processing the webhook event data and respond with a2xx
response. If they do not, reject the request with a401
or403
response. - Follow the steps in the How to Test Webhook Endpoints guide to ensure your endpoint receives data. If the signature validation is working properly, your endpoint will send a
2xx
response.
HMAC signature validation example
Below you will find an example written in NodeJS with ExpressJS utilizing the middleware pattern.
const express = require('express');
const app = express();
const port = 3000;
const { verifyAtomicWebhook } = require('./middleware');
app.post('/atomic-webhook', verifyAtomicWebhook, (req, res) => {
// process webhook data
// send us a 200 to let us know you received the data
res.send(200, 'Received webhook data');
});
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
});
const crypto = require('crypto')
exports.verifyAtomicWebhook = (req, res, next) => {
const hash = crypto
.createHmac('sha256', 'YOUR_ATOMIC_API_SECRET')
.update(req.body)
.digest('hex');
if (hash === req.header('X-HMAC-Signature-Sha-256')) {
next();
} else {
res.send(401, 'You are not authorized.');
}
};
IP address allowlist
For an additional layer of security, you can only allow requests from IPs that belong to Atomic. We publish our allowlist in the Atomic Console.