Http Request Smuggling in Adonisjs with Basic Auth
Http Request Smuggling in Adonisjs with Basic Auth — how this specific combination creates or exposes the vulnerability
HTTP Request Smuggling arises from inconsistent parsing of HTTP requests between frontend (e.g., a proxy or load balancer) and backend. In AdonisJS applications that use Basic Auth, the risk is heightened when authentication is applied at the framework level after the request has been accepted by an upstream gateway. If the gateway and AdonisJS interpret message boundaries differently—such as handling of Transfer-Encoding and Content-Length headers—an attacker can craft a request that is processed differently by each hop. This can cause a smuggled request to bypass intended routing, reach an authenticated endpoint without valid credentials, or be interpreted as part of an authenticated session.
With Basic Auth, credentials are sent in the Authorization header (e.g., Authorization: Basic base64(username:password)). If a gateway normalizes or splits headers differently than AdonisJS, a request that should require authentication might be processed by a handler that does not enforce it. For example, a gateway may treat a request with both Transfer-Chunked and Content-Length as two messages, forwarding one to AdonisJS while interpreting the other as part of a separate connection. AdonisJS may then parse the second message without re-evaluating authentication, allowing a smuggled request to execute under the context of the prior authenticated request. Common attack patterns like CL.TE (Content-Length then Transfer-Encoding) or TE.CL (Transfer-Encoding chunked then Content-Length) can exploit these inconsistencies when headers are not strictly validated or normalized before authentication checks.
AdonisJS does not inherently enforce strict header parsing rules for smuggling vectors; it relies on the underlying Node.js HTTP parser. If the application does not explicitly reject or normalize ambiguous headers, an unauthenticated path can be leveraged via a smuggled request to access endpoints that should require Basic Auth. This is particularly risky when APIs or admin routes are exposed behind a reverse proxy that does not strip or canonicalize headers before passing them to the application. To detect this, scans include checks for inconsistent handling of Transfer-Encoding and Content-Length alongside Authorization header presence, flagging endpoints where authentication may be bypassed due to smuggling.
Basic Auth-Specific Remediation in Adonisjs — concrete code fixes
To mitigate HTTP Request Smuggling risks when using Basic Auth in AdonisJS, ensure strict header normalization and validation before authentication logic runs. Always reject requests that contain both Content-Length and Transfer-Encoding headers, as these are classic indicators of smuggling attempts. Additionally, enforce canonical header names and avoid relying on duplicate or ambiguous header handling by the underlying server.
Below are concrete code examples for an AdonisJS application using Basic Auth. First, a route-level middleware that validates headers and rejects suspicious requests:
// start/hooks/basic-auth-smuggling-check.ts
import { Exception } from '@adonisjs/core/build/standalone'
export const validateBasicAuthHeaders = (ctx) => {
const req = ctx.request
const hasContentLength = req.has('content-length')
const hasTransferEncoding = req.has('transfer-encoding')
// Reject requests that exhibit signs of header smuggling
if (hasContentLength && hasTransferEncoding) {
throw new Exception('Smuggling indicators detected: Content-Length and Transfer-Encoding cannot both be present', 400, 'E_SMEG')
}
// Optionally enforce transfer-encoding: chunked only when appropriate
if (hasTransferEncoding) {
const te = req.header('transfer-encoding')
if (!te?.toLowerCase().includes('chunked')) {
throw new Exception('Unsupported Transfer-Encoding', 400, 'E_SMEG')
}
}
}
Second, integrate this middleware into your route definition alongside Basic Auth verification. This ensures headers are validated before credentials are checked:
// start/routes.ts
import Route from '@ioc:Adonis/Core/Route'
import { validateBasicAuthHeaders } from '#hooks/basic-auth-smuggling-check'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
Route.group(() => {
Route.get('/secure/data', validateBasicAuthHeaders, async (ctx: HttpContextContract) => {
const auth = ctx.request.auth()
if (!auth || auth.username !== 'admin' || auth.password !== 'secret') {
ctx.response.unauthorized()
return
}
return { message: 'secure data', user: auth.username }
}).middleware(['auth:basic'])
}).prefix('api')
Third, if you use a custom auth provider for Basic Auth, ensure it does not inadvertently trust headers set by upstream proxies. Explicitly read headers from the original request and avoid merging headers that could introduce duplicates:
// start/providers/basic_auth_provider.ts
import { Exception } from '@adonisjs/core/build/standalone'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export class BasicAuthProvider {
public async authenticate(ctx: HttpContextContract) {
const req = ctx.request
// Avoid using merged headers; prefer raw header access
const authorization = req.headers().authorization
if (!authorization || !authorization.startsWith('Basic ')) {
ctx.response.unauthorized()
return null
}
const encoded = authorization.split(' ')[1]
const decoded = Buffer.from(encoded, 'base64').toString('utf-8')
const [username, password] = decoded.split(':')
// Validate credentials securely (use env vars in practice)
if (username !== 'admin' || password !== 'secret') {
ctx.response.unauthorized()
return null
}
return { username, password }
}
}
These steps reduce the attack surface for smuggling by ensuring header consistency and validating message boundaries before authentication is applied. Combining these checks with regular scans—such as those provided by the middleBrick CLI or Web Dashboard—helps identify misconfigurations before they can be exploited in production.