JAVASCRIPT
Create a Server-Side Webhook Listener with Signature Verification
Set up an Express.js endpoint to securely receive and validate webhook payloads by verifying a shared secret signature, preventing tampering and unauthorized requests.
const express = require('express');
const bodyParser = require('body-parser');
const crypto = require('crypto');
const app = express();
const PORT = process.env.PORT || 3000;
// IMPORTANT: Use a strong, unique secret key for your webhook provider
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET || 'your_super_secret_webhook_key';
// Middleware to parse raw body for signature verification
// body-parser can parse JSON, but we need the raw body buffer for hashing
app.use(bodyParser.json({
verify: (req, res, buf) => {
req.rawBody = buf; // Store raw body for signature verification
}
}));
// Webhook endpoint
app.post('/webhook', (req, res) => {
const signatureHeader = req.headers['x-hub-signature'] || req.headers['x-github-signature'] || req.headers['stripe-signature'];
if (!signatureHeader) {
console.warn('Webhook received without signature header.');
return res.status(401).send('Unauthorized: No signature header.');
}
// For GitHub/GitLab: `sha1=HEX_DIGEST`
// For Stripe: `t=TIMESTAMP,v1=SIGNATURE` (need to parse timestamp and signature)
// This example focuses on a simple `sha256=HEX_DIGEST` or `sha1=HEX_DIGEST` format.
const [algorithm, signature] = signatureHeader.split('=');
if (!signature || !req.rawBody) {
console.error('Missing signature or raw body for verification.');
return res.status(400).send('Bad Request: Malformed signature or body.');
}
const hmac = crypto.createHmac(algorithm, WEBHOOK_SECRET);
hmac.update(req.rawBody);
const digest = hmac.digest('hex');
if (digest === signature) {
console.log('Webhook signature verified successfully!');
// Process the webhook payload
console.log('Received webhook payload:', req.body);
// Respond quickly to the webhook sender
res.status(200).send('Webhook received and verified.');
} else {
console.warn('Webhook signature verification failed!');
console.warn(`Expected: ${signature}, Got: ${digest}`);
res.status(403).send('Forbidden: Invalid signature.');
}
});
// Start the server
app.listen(PORT, () => {
console.log(`Webhook listener running on port ${PORT}`);
console.log(`Ensure WEBHOOK_SECRET environment variable is set for production.`);
});
How it works: This Node.js (Express) snippet demonstrates how to set up a server-side webhook listener with crucial signature verification. When integrating with third-party services via webhooks, it's vital to verify the payload's authenticity to prevent spoofing and tampering. The code uses `body-parser` to capture the raw request body and the `crypto` module to compute an HMAC signature. This computed signature is then compared against the signature provided in the webhook's header. If they match, the payload is considered valid and can be safely processed; otherwise, the request is rejected as unauthorized.