Http Request Smuggling in Adonisjs with Api Keys
Http Request Smuggling in Adonisjs with Api Keys — how this specific combination creates or exposes the vulnerability
HTTP request smuggling becomes relevant in AdonisJS applications when the framework sits behind a reverse proxy or API gateway that handles authentication via API keys. AdonisJS itself does not parse API keys; this responsibility typically falls to middleware that reads headers such as X-API-Key. If the middleware is implemented inconsistently across routes, or if the proxy and AdonisJS interpret message boundaries differently, an attacker can craft requests that cause the proxy to forward one request’s body to another route, bypassing intended authorization.
Consider a setup where an API key is validated by a proxy before the request reaches AdonisJS, and the application trusts the proxy’s routing decisions. A smuggling attempt might use mismatched Content-Length and Transfer-Encoding headers. The proxy may parse one request as two separate messages, while AdonisJS parses them as a single message. The second message, which may carry an authenticated path or admin action, can be routed to an endpoint that does not enforce the API key check, leading to BOLA-style access across tenant boundaries or privilege escalation.
In AdonisJS, routes defined in start/routes.ts can inadvertently expose this risk if route-specific middleware does not uniformly validate the API key on every verb and path. For example, if a middleware skips validation for OPTIONS preflight or health-check routes but enforces it on data endpoints, a smuggled request can exploit the inconsistency. The unauthenticated attack surface includes these unguarded paths, and an attacker can probe for them using black-box techniques supported by middleBrick’s unauthenticated scan checks.
An example of a vulnerable route group in AdonisJS might look like this, where API key validation is applied only to certain routes:
import Route from '@ioc:Adonis/Core/Route'
Route.group(() => {
Route.get('/public/info', async ({ response }) => {
response.send({ data: 'no auth required' })
})
Route.get('/secure/data', async ({ request, response }) => {
const apiKey = request.headers().get('x-api-key')
if (apiKey !== process.env.API_KEY) {
return response.unauthorized('invalid key')
}
response.send({ secret: 'top-secret' })
})
})
If a proxy normalizes or strips headers inconsistently, the /secure/data endpoint might be reached via a smuggled request that does not include x-api-key, especially if the proxy treats the request as two separate messages. This highlights why API key validation must be applied at a consistent layer and why unauthenticated surface scanning is important to detect such routing anomalies.
Api Keys-Specific Remediation in Adonisjs — concrete code fixes
Remediation focuses on consistent API key validation across all routes and ensuring that route parsing differences between the proxy and AdonisJS do not lead to smuggling opportunities.
- Apply API key validation as a global middleware so that every request, including preflight and health checks, is authenticated before reaching route handlers.
- Do not skip authentication for any verb or path that exists behind a proxy unless the proxy guarantees it normalizes messages identically to AdonisJS.
- Use explicit content-length handling and avoid relying on frameworks or proxies that mix
Content-LengthandTransfer-Encodingautomatically.
Here is an example of a global middleware in AdonisJS that validates API keys on every request:
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export default class ApiKeyAuthMiddleware {
public async handle(ctx: HttpContextContract, next: () => Promise<void>) {
const apiKey = ctx.request.headers().get('x-api-key')
if (apiKey !== process.env.API_KEY) {
return ctx.response.unauthorized({ error: 'invalid or missing api key' })
}
await next()
}
}
Register the middleware in start/kernel.ts and bind it globally:
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import ApiKeyAuthMiddleware from 'App/Middleware/ApiKeyAuth'
const middleware = {
global: [ApiKeyAuthMiddleware],
}
export default middleware
In your server configuration, ensure the proxy does not interfere with chunked encoding or message boundaries. If you use a gateway that terminates TLS and forwards to AdonisJS, configure it to pass through the original request body without normalization that mixes message parsing strategies.
For route-specific exceptions, explicitly apply the same middleware and avoid omitting routes from validation:
import Route from '@ioc:Adonis/Core/Route'
import ApiKeyAuthMiddleware from 'App/Middleware/ApiKeyAuth'
Route.get('/public/info', async ({ response }) => {
response.send({ info: 'public but consider auth if proxied consistently' })
}).middleware([ApiKeyAuthMiddleware])
Route.get('/secure/data', async ({ request, response }) => {
const apiKey = request.headers().get('x-api-key')
if (apiKey !== process.env.API_KEY) {
return response.unauthorized({ error: 'invalid key' })
}
response.send({ secret: 'top-secret' })
})
These steps reduce the risk that a proxy parses a request differently than AdonisJS, which is a common factor in request smuggling scenarios involving API keys.