GuardAPI Logo
GuardAPI

Fix Insecure Webhooks in Nuxt

Webhooks are remote triggers that bypass standard authentication flows. In Nuxt, exposing a public server route without HMAC signature verification is a critical vulnerability. Without verification, an attacker can spoof requests, bypass payment gateways, or trigger unauthorized internal logic by simply POSTing a JSON payload to your endpoint. To secure this, you must implement cryptographic signature validation.

The Vulnerable Pattern

export default defineEventHandler(async (event) => {
  // VULNERABLE: Directly trusting the parsed body without source verification
  const body = await readBody(event);

// Logic executes for any attacker who knows this URL await updateSubscriptionStatus(body.user_id, body.status);

return { success: true }; });

The Secure Implementation

The secure implementation relies on three security primitives. First, we use `readRawBody` instead of `readBody`. Parsed JSON can differ slightly from the original payload (spacing, encoding), which breaks the hash. Second, we use a shared secret known only to the provider (e.g., Stripe, GitHub) and our server to generate a local HMAC. Third, we use `timingSafeEqual`. Standard string comparisons (`===`) return early when a mismatch is found, allowing attackers to brute-force the signature character-by-character by measuring response times. This setup ensures that only the legitimate provider can trigger the event.

import { createHmac, timingSafeEqual } from 'node:crypto';

export default defineEventHandler(async (event) => { const config = useRuntimeConfig(); const signature = getHeader(event, ‘x-hub-signature-256’); const secret = config.webhookSecret;

// 1. Get raw body to ensure hash integrity const rawBody = await readRawBody(event, false);

if (!signature || !rawBody) { throw createError({ statusCode: 401, statusMessage: ‘Missing credentials’ }); }

// 2. Re-calculate HMAC-SHA256 const hmac = createHmac(‘sha256’, secret); const digest = Buffer.from(‘sha256=’ + hmac.update(rawBody).digest(‘hex’), ‘utf8’); const checksum = Buffer.from(signature, ‘utf8’);

// 3. Constant-time comparison to prevent timing attacks if (checksum.length !== digest.length || !timingSafeEqual(digest, checksum)) { throw createError({ statusCode: 401, statusMessage: ‘Invalid signature’ }); }

const body = JSON.parse(rawBody.toString()); await updateSubscriptionStatus(body.user_id, body.status);

return { status: ‘verified’ }; });

System Alert • ID: 3990
Target: Nuxt API
Potential Vulnerability

Your Nuxt API might be exposed to Insecure Webhooks

74% of Nuxt 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.