GuardAPI Logo
GuardAPI

Fix Insecure Webhooks in Sails

Webhooks are essentially unauthenticated POST endpoints exposed to the public internet. If you aren't verifying the payload signature, you're inviting attackers to spoof events, manipulate data, or escalate privileges. In Sails, the default body parser makes it easy to blindly trust incoming JSON. Real security requires a cryptographic handshake using HMAC and a shared secret.

The Vulnerable Pattern

// api/controllers/WebhookController.js
module.exports = async function (req, res) {
  // VULNERABILITY: Blindly trusting the request body
  const { userId, eventType } = req.body;

if (eventType === ‘payment_success’) { await User.updateOne({ id: userId }).set({ plan: ‘premium’ }); }

return res.ok(); };

The Secure Implementation

The fix involves three layers of defense: 1. A shared secret known only to the provider and your Sails app. 2. HMAC-SHA256 signature verification to ensure the payload hasn't been tampered with or spoofed. 3. Using `crypto.timingSafeEqual` to compare signatures, which prevents attackers from guessing the secret byte-by-byte via execution time analysis. For production environments, ensure you use the raw request body if your middleware modifies `req.body` during parsing.

// api/controllers/WebhookController.js
const crypto = require('crypto');

module.exports = async function (req, res) { const signature = req.headers[‘x-hub-signature-256’]; const secret = sails.config.custom.webhookSecret;

if (!signature) { return res.forbidden(‘Missing signature’); }

// Calculate HMAC SHA256 const hmac = crypto.createHmac(‘sha256’, secret); const digest = ‘sha256=’ + hmac.update(JSON.stringify(req.body)).digest(‘hex’);

// Use timingSafeEqual to prevent side-channel timing attacks const trusted = Buffer.from(digest, ‘ascii’); const untrusted = Buffer.from(signature, ‘ascii’);

if (trusted.length !== untrusted.length || !crypto.timingSafeEqual(trusted, untrusted)) { return res.forbidden(‘Invalid signature’); }

// Payload is verified; proceed with logic const { userId, eventType } = req.body; if (eventType === ‘payment_success’) { await User.updateOne({ id: userId }).set({ plan: ‘premium’ }); }

return res.ok(); };

System Alert • ID: 4456
Target: Sails API
Potential Vulnerability

Your Sails API might be exposed to Insecure Webhooks

74% of Sails apps fail this check. Hackers use automated scanners to find this specific flaw. Check your codebase before they do.

RUN FREE SECURITY DIAGNOSTIC
GuardLabs Engine: ONLINE

Free Tier • No Credit Card • Instant Report

Verified by Ghost Labs Security Team

This content is continuously validated by our automated security engine and reviewed by our research team. Ghost Labs analyzes over 500+ vulnerability patterns across 40+ frameworks to provide up-to-date remediation strategies.