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.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs