JAVASCRIPT
Create a Secure Webhook Endpoint with Signature Verification
Set up a Node.js Express webhook endpoint to securely receive real-time API updates, including signature verification for data integrity and authenticity.
const express = require('express');
const bodyParser = require('body-parser');
const crypto = require('crypto');
const app = express();
const port = process.env.PORT || 3000;
// IMPORTANT: Use raw body parser for webhook signature verification
// This parses the body into a Buffer, required for HMAC hashing
app.use(bodyParser.json({
verify: (req, res, buf) => {
req.rawBody = buf; // Store the raw body for signature verification
}
}));
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET || 'your_secret_key_here'; // Must match the secret configured in the API provider
// Function to verify the webhook signature
function verifyWebhookSignature(payload, signatureHeader, secret) {
if (!signatureHeader) {
return false; // No signature provided
}
// Example: GitHub style signature header 'sha256=<signature>'
const parts = signatureHeader.split('=');
if (parts.length !== 2 || parts[0] !== 'sha256') {
console.error('Invalid signature header format:', signatureHeader);
return false;
}
const algorithm = parts[0]; // e.g., sha256
const providedSignature = parts[1];
const hmac = crypto.createHmac(algorithm, secret);
const digest = hmac.update(payload).digest('hex');
// Compare the computed signature with the provided signature
// Using crypto.timingSafeEqual to prevent timing attacks
return crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(providedSignature));
}
app.post('/webhook', (req, res) => {
const signature = req.headers['x-hub-signature-256'] || req.headers['x-webhook-signature']; // Adjust header name as per API provider
const rawPayload = req.rawBody; // The raw body buffer from bodyParser.json verify option
if (!rawPayload) {
console.error('Raw body not available for signature verification.');
return res.status(400).send('Webhook error: Raw body missing.');
}
if (!verifyWebhookSignature(rawPayload, signature, WEBHOOK_SECRET)) {
console.error('Webhook signature verification failed!');
return res.status(401).send('Unauthorized: Invalid signature.');
}
console.log('Webhook signature verified successfully!');
const eventData = req.body;
console.log('Received webhook event:', eventData);
// Process the eventData here
// e.g., update database, trigger other services, etc.
res.status(200).send('Webhook received and processed.');
});
app.listen(port, () => {
console.log(`Webhook listener running on port ${port}`);
console.log(`Access at http://localhost:${port}/webhook`);
});
How it works: This Node.js Express snippet sets up a robust webhook endpoint to receive real-time updates from an external API. Crucially, it includes signature verification using `crypto` to ensure that incoming requests are legitimate and haven't been tampered with. The `bodyParser.json` middleware is configured to store the raw request body, which is essential for calculating the HMAC signature. The `verifyWebhookSignature` function compares the generated signature with the one provided in the request headers (e.g., `X-Hub-Signature-256`), preventing unauthorized or malicious payloads from being processed.