JAVASCRIPT

Verifying Webhook Signatures in Node.js Express

Create a secure webhook endpoint in Node.js Express. This snippet verifies incoming requests using a shared secret signature header to prevent unauthorized access and spoofing.

const express = require('express');
const crypto = require('crypto');
const bodyParser = require('body-parser');

const app = express();
const PORT = 3000;

// IMPORTANT: Your secret key shared with the webhook sender
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET || 'super_secret_key_123';

// Middleware to parse raw body for signature verification
// Must be placed BEFORE any other body-parsing middleware like express.json()
app.use(bodyParser.raw({ type: 'application/json' }));

// Webhook endpoint
app.post('/webhook', (req, res) => {
  const signature = req.headers['x-signature'] || req.headers['stripe-signature']; // Adjust header name as per API
  const payload = req.body.toString('utf8'); // The raw body as a string

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

  // Calculate expected signature using HMAC-SHA256 (common algorithm)
  // Adjust algorithm ('sha256', 'sha1', etc.) and encoding ('hex', 'base64') as per API docs
  const hmac = crypto.createHmac('sha256', WEBHOOK_SECRET);
  hmac.update(payload);
  const expectedSignature = hmac.digest('hex');

  // Compare signatures securely
  const isSignatureValid = crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );

  if (!isSignatureValid) {
    console.warn('Webhook signature verification failed!');
    return res.status(403).send('Invalid signature.');
  }

  // If signature is valid, parse the JSON payload and process the event
  let event;
  try {
    event = JSON.parse(payload); // Now it's safe to parse
  } catch (err) {
    return res.status(400).send('Invalid JSON payload.');
  }

  console.log('Webhook received and verified!', event.type);
  // Process your webhook event here (e.g., update database, trigger actions)
  res.status(200).send('Webhook received and processed.');
});

app.listen(PORT, () => {
  console.log(`Webhook server listening on port ${PORT}`);
});
How it works: This Node.js Express snippet demonstrates how to create a secure webhook receiver. It uses `body-parser` to get the raw request body, which is crucial for signature verification. The incoming request's signature (e.g., from `x-signature` header) is compared against a locally computed signature using `crypto.createHmac` and a shared `WEBHOOK_SECRET`. `crypto.timingSafeEqual` is used for a secure comparison to prevent timing attacks. Only if the signatures match is the payload parsed and processed, ensuring the request genuinely originated from the trusted API source.

Need help integrating this into your project?

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

Hire DigitalCodeLabs