JAVASCRIPT
Secure Webhook Verification using HMAC Signatures
Implement secure webhook verification in Node.js with Express by computing and comparing HMAC signatures, protecting your API endpoints from unauthorized or tampered webhook payloads.
const express = require('express');
const crypto = require('crypto');
const bodyParser = require('body-parser'); // For parsing raw body
const app = express();
const port = 3000;
// IMPORTANT: Use the actual secret provided by the webhook sender
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET || 'your_super_secret_key_here';
// Middleware to parse raw body for signature verification
// This must be applied before any other body parsing middleware (like json())
app.use(bodyParser.raw({ type: 'application/json' }));
// Webhook endpoint
app.post('/webhook', (req, res) => {
const signatureHeader = req.headers['x-hub-signature'] || req.headers['x-github-signature']; // Example headers
const receivedSignature = signatureHeader ? signatureHeader.split('=')[1] : null;
if (!receivedSignature) {
return res.status(401).send('No signature header found');
}
// Reconstruct the payload as a string
// req.body contains the raw buffer if bodyParser.raw is used
const payload = req.body.toString('utf8');
// Compute our own HMAC signature
const hmac = crypto.createHmac('sha256', WEBHOOK_SECRET);
hmac.update(payload);
const computedSignature = hmac.digest('hex');
// Compare the signatures
// Use crypto.timingSafeEqual to prevent timing attacks
const isSignatureValid = crypto.timingSafeEqual(
Buffer.from(computedSignature, 'utf8'),
Buffer.from(receivedSignature, 'utf8')
);
if (!isSignatureValid) {
console.warn('Webhook received with invalid signature!');
return res.status(403).send('Invalid signature');
}
// If signature is valid, parse the JSON payload and process the webhook
const eventData = JSON.parse(payload);
console.log('Webhook received and verified!', eventData);
// Process your webhook event here...
res.status(200).send('Webhook received successfully');
});
app.listen(port, () => {
console.log(`Webhook server listening at http://localhost:${port}`);
});
How it works: This Node.js Express snippet demonstrates how to securely verify incoming webhooks using HMAC (Hash-based Message Authentication Code) signatures. It uses `body-parser.raw` to get the raw request body, which is then used to compute a local HMAC signature with a shared secret key. This computed signature is then compared against the signature provided in the webhook's `x-hub-signature` (or similar) header. `crypto.timingSafeEqual` is used for comparison to mitigate timing attacks, ensuring the webhook's authenticity and integrity.