GuardAPI Logo
GuardAPI

Fix Insecure Webhooks in NestJS

Webhooks are unauthenticated entry points by design. If you aren't verifying the cryptographic signature of the incoming payload, you're effectively exposing a public API that allows any script kiddie to spoof events and manipulate your internal state. Stop trusting the 'origin' header and start verifying the HMAC.

The Vulnerable Pattern

@Controller('webhooks')
export class WebhookController {
  @Post('stripe')
  handleWebhook(@Body() payload: any) {
    // VULNERABILITY: No signature verification.
    // Anyone can POST a fake 'payment.succeeded' event to this endpoint.
    return this.paymentService.fulfillOrder(payload.data.object.customer_email);
  }
}

The Secure Implementation

The fix involves three critical steps. First, enable 'rawBody: true' in your main.ts NestJS factory to access the unmutated payload; verifying a signature against a pre-parsed JSON object will fail due to whitespace differences. Second, compute the HMAC-SHA256 hash using your shared secret and the raw body. Finally, use 'crypto.timingSafeEqual' for the comparison. Standard string comparison (==) is vulnerable to timing attacks where an attacker can determine the correct signature byte-by-byte based on response latency.

import { Controller, Post, Headers, Req, BadRequestException, RawBodyRequest } from '@nestjs/common';
import { createHmac, timingSafeEqual } from 'crypto';

@Controller(‘webhooks’) export class WebhookController { @Post(‘secure-handler’) async handle(@Headers(‘x-signature’) signature: string, @Req() req: RawBodyRequest) { const secret = process.env.WEBHOOK_SECRET; if (!signature) throw new BadRequestException(‘Missing signature’);

// 1. Must use the raw body, not the parsed JSON object, for HMAC integrity
const hmac = createHmac('sha256', secret);
const digest = Buffer.from(hmac.update(req.rawBody).digest('hex'), 'utf8');
const checksum = Buffer.from(signature, 'utf8');

// 2. Use timingSafeEqual to prevent side-channel timing attacks
if (checksum.length !== digest.length || !timingSafeEqual(digest, checksum)) {
  throw new BadRequestException('Invalid signature');
}

return this.processEvent(JSON.parse(req.rawBody.toString()));

} }

System Alert • ID: 6721
Target: NestJS API
Potential Vulnerability

Your NestJS API might be exposed to Insecure Webhooks

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