Fix Insecure Webhooks in LoopBack
Webhooks are essentially unauthenticated POST endpoints unless you implement cryptographic verification. In LoopBack applications, accepting webhook payloads without validating the source signature allows attackers to spoof events, manipulate internal state, or bypass business logic. To harden this, you must enforce HMAC (Hash-based Message Authentication Code) verification using a shared secret and timing-safe comparisons.
The Vulnerable Pattern
import {post, requestBody} from '@loopback/rest';
export class WebhookController { @post(‘/webhooks/stripe’) async handle(@requestBody() payload: any): Promise{ // VULNERABLE: No signature verification. // Anyone can POST any JSON to this endpoint to trigger logic. if (payload.type === ‘charge.succeeded’) { await this.fulfillOrder(payload.data.object.id); } } }
The Secure Implementation
The secure implementation introduces a cryptographic handshake. 1. Shared Secret: We use a secret key known only to the provider and our app. 2. HMAC Generation: We hash the incoming request body using SHA-256. 3. Constant-Time Comparison: We use crypto.timingSafeEqual instead of a standard equality operator (==) to prevent attackers from guessing the signature via response time variances. This ensures that only the legitimate provider can trigger the webhook logic.
import {post, requestBody, HttpErrors, Request, RestBindings} from '@loopback/rest'; import {inject} from '@loopback/core'; import * as crypto from 'crypto';export class WebhookController { constructor(@inject(RestBindings.Http.REQUEST) private req: Request) {}
@post(‘/webhooks/stripe’) async handle(@requestBody() payload: any): Promise
{ const signature = this.req.headers[‘x-hub-signature-256’] as string; const secret = process.env.WEBHOOK_SECRET || ”; if (!signature) throw new HttpErrors.Unauthorized('Missing Signature'); // Recompute HMAC using the raw body and shared secret const hmac = crypto.createHmac('sha256', secret); const digest = Buffer.from('sha256=' + hmac.update(JSON.stringify(payload)).digest('hex'), 'utf8'); const checksum = Buffer.from(signature, 'utf8'); // Use timingSafeEqual to mitigate timing side-channel attacks if (checksum.length !== digest.length || !crypto.timingSafeEqual(digest, checksum)) { throw new HttpErrors.Unauthorized('Invalid Signature'); } await this.processVerifiedEvent(payload);
} }
Your LoopBack API
might be exposed to Insecure Webhooks
74% of LoopBack apps fail this check. Hackers use automated scanners to find this specific flaw. Check your codebase before they do.
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.