JAVASCRIPT

Secure Webhook Verification using HMAC Signatures

Implement secure webhook verification in Node.js with Express by computing and comparing HMAC signatures, protecting your API endpoints from unauthorized or tampered webhook payloads.

const express = require('express');
const crypto = require('crypto');
const bodyParser = require('body-parser'); // For parsing raw body

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

// IMPORTANT: Use the actual secret provided by the webhook sender
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET || 'your_super_secret_key_here';

// Middleware to parse raw body for signature verification
// This must be applied before any other body parsing middleware (like json())
app.use(bodyParser.raw({ type: 'application/json' }));

// Webhook endpoint
app.post('/webhook', (req, res) => {
  const signatureHeader = req.headers['x-hub-signature'] || req.headers['x-github-signature']; // Example headers
  const receivedSignature = signatureHeader ? signatureHeader.split('=')[1] : null;

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

  // Reconstruct the payload as a string
  // req.body contains the raw buffer if bodyParser.raw is used
  const payload = req.body.toString('utf8');

  // Compute our own HMAC signature
  const hmac = crypto.createHmac('sha256', WEBHOOK_SECRET);
  hmac.update(payload);
  const computedSignature = hmac.digest('hex');

  // Compare the signatures
  // Use crypto.timingSafeEqual to prevent timing attacks
  const isSignatureValid = crypto.timingSafeEqual(
    Buffer.from(computedSignature, 'utf8'),
    Buffer.from(receivedSignature, 'utf8')
  );

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

  // If signature is valid, parse the JSON payload and process the webhook
  const eventData = JSON.parse(payload);
  console.log('Webhook received and verified!', eventData);
  // Process your webhook event here...

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

app.listen(port, () => {
  console.log(`Webhook server listening at http://localhost:${port}`);
});
How it works: This Node.js Express snippet demonstrates how to securely verify incoming webhooks using HMAC (Hash-based Message Authentication Code) signatures. It uses `body-parser.raw` to get the raw request body, which is then used to compute a local HMAC signature with a shared secret key. This computed signature is then compared against the signature provided in the webhook's `x-hub-signature` (or similar) header. `crypto.timingSafeEqual` is used for comparison to mitigate timing attacks, ensuring the webhook's authenticity and integrity.

Need help integrating this into your project?

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

Hire DigitalCodeLabs