HIGH http request smugglingadonisjsmutual tls

Http Request Smuggling in Adonisjs with Mutual Tls

Http Request Smuggling in Adonisjs with Mutual Tls

Http Request Smuggling arises from inconsistent parsing of HTTP messages, typically when a frontend (e.g., a reverse proxy or load balancer using Mutual TLS) and Adonisjs handle message boundaries differently. In a Mutual TLS setup, the TLS termination occurs at the edge, and the proxy presents client certificates to Adonisjs. If the proxy normalizes or splits requests before forwarding—such as combining multiple requests in a pipeline (CL.TE or TE.CL scenarios)—and Adonisjs parses the raw stream with its own HTTP parser, the boundary assumptions can diverge. Adonisjs may trust the Content-Length header from the proxy while the proxy has already consumed part of the next message, enabling request splitting to leak authentication tokens or elevate privileges across tenant boundaries.

Mutual TLS itself does not cause smuggling, but it changes the trust boundary. When client certificates are verified at the proxy, the proxy may rewrite headers (e.g., stripping hop-by-hop headers) before forwarding. If Adonisjs receives a forwarded request with a mismatched Content-Length and Transfer-Encoding, it may process the request as a single message while the proxy has buffered an adjacent request. This can lead to request smuggling attacks like request concatenation or cache poisoning, where an attacker crafts a request that is interpreted differently by the proxy and the framework. In Adonisjs, routes that rely on strict header validation or assume a one-to-one mapping between incoming requests and application-level routes become vulnerable when the transport layer obscures message boundaries.

For example, an attacker could send a request like:

POST /api/users HTTP/1.1
Content-Length: 47
Transfer-Encoding: chunked

0
POST /api/admin HTTP/1.1
Host: api.example.com
Content-Length: 11

secret=123

If the Mutual TLS proxy interprets the first request as ending after 47 bytes and forwards the remainder as a new request to Adonisjs, the framework may process the second POST as if it were part of the first, bypassing intended access controls. This is especially risky when Adonisjs handles sensitive operations (e.g., admin routes) without validating the original TLS context, since the proxy’s certificate validation does not automatically enforce framework-level authorization checks.

Mutual Tls-Specific Remediation in Adonisjs

To mitigate Http Request Smuggling in Adonisjs when Mutual TLS is in use, ensure strict header parsing and avoid trusting upstream headers that may have been altered by the proxy. Configure Adonisjs to reject ambiguous or conflicting Transfer-Encoding and Content-Length headers. You should also enforce that the framework validates the original client certificate on each request, rather than relying solely on proxy indicators.

Below are concrete code examples for Adonisjs that demonstrate Mutual TLS configuration and defensive request handling.

Mutual TLS setup in Adonisjs (server.ts)

import { defineConfig } from '@adonisjs/core/app'
import { HttpsConfig } from '@adonisjs/core/server'

export default defineConfig({
  https: {
    enable: true,
    config: {
      key: '/path/to/server.key',
      cert: '/path/to/server.crt',
      ca: '/path/to/ca.crt',
      requestCert: true,
      rejectUnauthorized: true,
    } satisfies HttpsConfig
  }
})

This configuration ensures that the server requests and validates client certificates. rejectUnauthorized: true causes Adonisjs to reject connections where the client certificate cannot be verified against the provided CA, adding a strong transport-layer guarantee.

Strict header validation middleware

import { Exception } from '@adonisjs/core/exceptions'
import { HttpContextContract } from '@adonisjs/core/http'

export default class RequestValidationMiddleware {
  public async handle(ctx: HttpContextContract, next: () => Promise) {
    const request = ctx.request
    const hasContentLength = request.has('content-length')
    const hasTransferEncoding = request.has('transfer-encoding')

    // Reject requests with both headers (ambiguous parsing)
    if (hasContentLength && hasTransferEncoding) {
      throw new Exception('Conflicting Content-Length and Transfer-Encoding headers', 400, 'E_INVALID_HEADERS')
    }

    // Optionally enforce strict header canonicalization
    if (hasTransferEncoding) {
      const te = request.header('transfer-encoding')
      if (te && !te.toLowerCase().includes('chunked')) {
        throw new Exception('Unsupported Transfer-Encoding', 400, 'E_INVALID_HEADERS')
      }
    }

    await next()
  }
}

This middleware runs before route handlers and rejects requests that could trigger smuggling due to header conflicts. By enforcing a clear parsing rule, you reduce the risk that proxy-induced header transformations lead to inconsistent interpretation.

Finally, ensure that any proxy in front of Adonisjs is configured to normalize or remove hop-by-hop headers and to maintain a consistent interpretation of message boundaries. Combine this with the framework-level checks above to create a robust defense against request smuggling in Mutual TLS environments.

Frequently Asked Questions

Does Mutual TLS prevent Http Request Smuggling in Adonisjs?
No. Mutual TLS secures transport authentication but does not prevent header parsing inconsistencies between the proxy and Adonisjs, which can still enable request smuggling.
How can I test if my Adonisjs app is vulnerable to request smuggling with Mutual TLS?
Use a tool that sends smuggled request pairs (e.g., CL.TE or TE.CL) with valid client certificates. Inspect whether Adonisjs processes unintended actions or leaks data across request boundaries.