JAVASCRIPT

Verify Webhook Signatures for Secure API Integrations

Secure your Node.js webhook endpoints by implementing signature verification to authenticate payloads and prevent processing of malicious or tampered requests.

// server.js (Node.js with Express)
const express = require('express');
const crypto = require('crypto');
const bodyParser = require('body-parser'); // Deprecated, but still common for raw body
require('dotenv').config();

const app = express();
const PORT = process.env.PORT || 3002;

const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET || 'super_secret_key'; // Use a strong, unique secret

// Middleware to get the raw body, crucial for signature verification
app.use(bodyParser.json({
    verify: (req, res, buf) => {
        req.rawBody = buf; // Store the raw body for signature verification
    }
}));

// Webhook endpoint with signature verification
app.post('/webhook/my-service', (req, res) => {
    const signature = req.headers['x-signature-header-name']; // e.g., 'x-hub-signature', 'stripe-signature'

    if (!signature) {
        return res.status(401).send('No signature header provided.');
    }

    // Calculate expected signature using the raw body and secret
    const hmac = crypto.createHmac('sha256', WEBHOOK_SECRET);
    hmac.update(req.rawBody);
    const expectedSignature = `sha256=${hmac.digest('hex')}`; // Adjust prefix if needed (e.g., 'v1=' for GitHub)

    // Compare signatures (using a timing-safe comparison is best practice)
    // crypto.timingSafeEqual requires buffers of equal length
    const isSignatureValid = crypto.timingSafeEqual(
        Buffer.from(signature),
        Buffer.from(expectedSignature)
    );

    if (!isSignatureValid) {
        console.warn('Webhook signature mismatch:', signature);
        return res.status(403).send('Invalid webhook signature.');
    }

    // If signature is valid, process the webhook payload
    console.log('Webhook received and verified:', req.body);
    // Implement your specific webhook logic here

    res.status(200).send('Webhook received and processed.');
});

app.listen(PORT, () => {
    console.log(`Webhook server running on http://localhost:${PORT}`);
});

// Example .env file:
// WEBHOOK_SECRET=your_webhook_super_secret_key_here
How it works: This Node.js Express snippet demonstrates how to securely receive webhooks by verifying their signatures. It uses `body-parser` to capture the raw request body, which is essential for calculating the HMAC signature. The calculated signature is then compared with the signature provided in the webhook header, ensuring that the payload hasn't been tampered with and originates from a trusted source, adding a critical layer of security to your API integrations.

Need help integrating this into your project?

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

Hire DigitalCodeLabs