Webhook Spoofing in Adonisjs
How Webhook Spoofing Manifests in Adonisjs
In Adonisjs applications, webhook spoofing typically occurs when an endpoint receives external notifications (e.g., from payment gateways like Stripe or version control platforms like GitHub) without properly validating the request's origin. Attackers exploit this by forging legitimate-looking requests that bypass trust assumptions, leading to unauthorized state changes such as fraudulent order fulfillment or repository manipulation. This vulnerability maps to OWASP API Security Top 10:2023 API1:2023 Broken Object Level Authorization when combined with insufficient identity validation.
A common Adonisjs-specific pattern involves route handlers in app/Controllers/Http/WebhookController.ts that directly process payloads without verifying cryptographic signatures. For example, a Stripe webhook endpoint might look like this:
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export default class WebhookController {
public async handle({ request, response }: HttpContextContract) {
const payload = request.body()
// Missing signature verification
if (payload.type === 'payment_intent.succeeded') {
await this.fulfillOrder(payload.data.object.metadata.orderId)
}
return response.send({ received: true })
}
}
Here, the absence of request.header('stripe-signature') validation allows attackers to spoof events by guessing or leaking endpoint URLs. Similarly, GitHub webhook spoofing exploits missing X-Hub-Signature-256 checks. Adonisjs does not include built-in webhook validation middleware, placing the burden on developers to implement verification per provider specifications. This gap is frequently introduced during rapid integration, where teams prioritize functionality over security hardening of inbound webhooks.
Adonisjs-Specific Detection
To remediate webhook spoofing in Adonisjs applications, developers must implement provider-specific request validation using cryptographic signatures or shared secrets. Adonisjs does not include built-in webhook validation, so leveraging its native features—such as the request object, environment configuration, and exception handling—is essential. Below are provider-agnostic and Stripe-specific examples demonstrating correct validation patterns.
First, store webhook secrets securely in .env (never commit to version control):
STRIPE_WEBHOOK_SECRET=whsec_...
GITHUB_WEBHOOK_SECRET=your_shared_secret
Then, implement validation in the controller. For Stripe webhooks, use the official Stripe Node library to verify signatures:
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import Stripe from 'stripe'
const stripe = new Stripe(Env.get('STRIPE_SECRET_KEY'), { apiVersion: '2023-10-16' })
export default class WebhookController {
public async handle({ request, response }: HttpContextContract) {
const sig = request.header('stripe-signature')
if (!sig) {
return response.badRequest({ error: 'Missing Stripe signature' })
}
let event
try {
event = stripe.webhooks.constructEvent(
request.rawBody(),
sig,
Env.get('STRIPE_WEBHOOK_SECRET')
)
} catch (err) {
return response.badRequest({ error: `Webhook Error: ${err.message}` })
}
// Process verified event
if (event.type === 'payment_intent.succeeded') {
await this.fulfillOrder(event.data.object.metadata.orderId)
}
return response.send({ received: true })
}
}
Note the use of request.rawBody()—critical for signature verification, as Adonisjs' request.body() parses JSON and alters the raw payload. For GitHub webhooks, validate using HMAC-SHA256:
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import crypto from 'crypto'
export default class GithubWebhookController {
public async handle({ request, response }: HttpContextContract) {
const signature = request.header('x-hub-signature-256')
if (!signature) {
return response.badRequest({ error: 'Missing GitHub signature' })
}
const hmac = crypto.createHmac('sha256', Env.get('GITHUB_WEBHOOK_SECRET'))
const digest = `sha256=${hmac.update(request.rawBody()).digest('hex')}`
if (!crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(signature))) {
return response.badRequest({ error: 'Invalid GitHub signature' })
}
const payload = request.body()
// Process verified payload (e.g., handle pull_request events)
return response.send({ received: true })
}
}
These implementations use Adonisjs' Env for secure secret access and request.rawBody() to preserve payload integrity. Always return HTTP 400 for invalid signatures—never 200—to prevent retry loops from attackers. For broader coverage, consider creating a custom middleware to encapsulate validation logic, though provider-specific differences often necessitate per-endpoint handling. By adopting these patterns, Adonisjs applications cryptographically verify webhook origins, eliminating spoofing risks while maintaining compatibility with third-party services.
Frequently Asked Questions
Does middleBrick scan for webhook spoofing in Adonisjs applications by default?
Can I use middleBrick's CLI to test a local Adonisjs development server for webhook vulnerabilities?
npm i -g middlebrick), you can scan a locally running Adonisjs server by providing its URL—for example, middlebrick scan http://localhost:3333/webhook/stripe. The tool performs the same unauthenticated black-box checks as the dashboard, reporting findings without requiring agents, configuration, or access to your source code.