GuardAPI Logo
GuardAPI
GuardAPI Logo GuardAPI

Fix Insecure Webhooks in AdonisJS

Webhooks are high-value targets for spoofing and replay attacks. In AdonisJS, if you process incoming webhooks without cryptographic verification, you're effectively exposing internal logic to the public internet. Attackers can forge events, manipulate data, or trigger unintended side effects by simply POSTing a JSON payload to your endpoint. To secure this, you must implement HMAC signature verification using the raw request body.

The Vulnerable Pattern

import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'

export default class WebhooksController { public async handle({ request, response }: HttpContextContract) { // DANGER: Blindly trusting the parsed body const payload = request.all()

if (payload.type === 'payment.succeeded') {
  // This logic can be triggered by anyone with the URL
  await fulfillOrder(payload.data.orderId)
}

return response.ok({ status: 'success' })

} }

The Secure Implementation

The secure implementation relies on three critical security principles. First, we use `request.raw()` because standard body parsers can modify the spacing or key order of JSON, which invalidates the HMAC. Second, we generate a local hash using a shared secret and the SHA-256 algorithm. Third, we use `crypto.timingSafeEqual` to compare the signatures; standard string comparison (`===`) is vulnerable to timing attacks where an attacker can determine the correct signature one character at a time by measuring the response latency.

import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import crypto from 'crypto'
import Env from '@ioc:Adonis/Core/Env'

export default class WebhooksController { public async handle({ request, response }: HttpContextContract) { const signature = request.header(‘X-Webhook-Signature’) const secret = Env.get(‘WEBHOOK_SECRET’) const rawBody = request.raw() // Essential: use the unparsed body

if (!signature || !rawBody) {
  return response.unauthorized('Missing signature')
}

const hmac = crypto.createHmac('sha256', secret)
const expectedSignature = hmac.update(rawBody).digest('hex')

// Prevent timing attacks with timingSafeEqual
const isValid = crypto.timingSafeEqual(
  Buffer.from(signature),
  Buffer.from(expectedSignature)
)

if (!isValid) {
  return response.badRequest('Invalid signature')
}

const payload = JSON.parse(rawBody)
// Process verified payload...
return response.ok({ status: 'verified' })

} }

Protect your AdonisJS API

Don't rely on manual checks. GuardAPI's Gemini 3 Pro engine detects Insecure Webhooks and logic flaws in seconds.

Run Automated Audit

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.