JAVASCRIPT
Verify Webhook Signatures with HMAC-SHA256 in Node.js
Secure your Node.js webhook endpoints by verifying incoming signatures using HMAC-SHA256, ensuring data integrity and preventing spoofing.
const crypto = require('crypto');
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET || 'YOUR_SECURE_WEBHOOK_SECRET'; // Use a strong secret!
// IMPORTANT: Parse the raw body for signature verification BEFORE other body parsers
// Use a custom body parser for raw text. Limit size if needed.
app.use(bodyParser.raw({ type: 'application/json', limit: '5mb' }));
function verifyWebhookSignature(req, res, next) {
const signatureHeader = req.headers['x-hub-signature'] || req.headers['x-github-signature'] || req.headers['stripe-signature'];
const signaturePrefix = 'sha256='; // Or 'v1=' for Stripe, etc.
if (!signatureHeader || !signatureHeader.startsWith(signaturePrefix)) {
console.warn('Missing or invalid signature header.');
return res.status(401).send('Unauthorized: Missing or invalid signature.');
}
const expectedSignature = signatureHeader.substring(signaturePrefix.length);
// `req.body` here is the raw buffer from `bodyParser.raw`
const payload = req.body.toString('utf8');
const hmac = crypto.createHmac('sha256', WEBHOOK_SECRET);
const digest = hmac.update(payload).digest('hex');
if (crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(expectedSignature))) {
// Re-parse the body as JSON for subsequent middlewares
req.body = JSON.parse(payload);
next();
} else {
console.warn('Webhook signature mismatch. Potential tampering detected.');
res.status(403).send('Forbidden: Invalid signature.');
}
}
app.post('/webhook', verifyWebhookSignature, (req, res) => {
console.log('Received verified webhook payload:', req.body);
// Process the webhook payload here
res.status(200).send('Webhook received and verified.');
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Webhook server listening on port ${PORT}`);
});
How it works: This Node.js snippet demonstrates how to verify incoming webhook signatures using HMAC-SHA256, a critical security measure for API integrations. It uses `crypto` to compute a hash of the raw request body with a shared secret and compares it against the signature provided in the webhook header (e.g., `X-Hub-Signature`). This ensures that the webhook originated from a trusted source and its payload hasn't been tampered with, preventing unauthorized or malicious requests.