Request Smuggling in Adonisjs with Cockroachdb
Request Smuggling in Adonisjs with Cockroachdb — how this specific combination creates or exposes the vulnerability
Request smuggling occurs when an application processes HTTP requests differently in transit versus at the backend, allowing attackers to smuggle requests across security boundaries. In a setup where Adonisjs (a Node.js web framework) communicates with Cockroachdb (a distributed SQL database), the risk arises not from Cockroachdb itself, but from how Adonisjs parses and forwards requests—especially when behind a proxy or load balancer that handles HTTP/1.1 and HTTP/2 differently.
Adonisjs typically relies on its HTTP server layer to parse incoming requests, build query parameters, and bind them to ORM models before interacting with Cockroachdb. If the front-facing proxy normalizes or splits headers differently than Adonisjs, an attacker can craft a request with conflicting Content-Length and Transfer-Encoding headers. Adonisjs may interpret the request body one way while the proxy or downstream service interprets it another, enabling a smuggled request to reach Cockroachdb-bound routes that assume a trusted context.
For example, an attacker could send a request that, when parsed by Adonisjs, results in a second request being routed to an administrative endpoint that performs sensitive Cockroachdb operations (e.g., executing raw migrations or altering rows). Because Adonisjs may not validate that the intended route matches the actual executed route in all intermediate proxies, the smuggled request can execute with unintended permissions. The Cockroachdb driver in Adonisjs then processes queries based on the attacker’s injected request path or parameters, potentially exposing data or modifying records due to insufficient route binding validation.
Specific patterns that increase risk include: using untrusted proxies that support both HTTP/1.1 and HTTP/2 without strict header validation, failing to enforce a single protocol for API endpoints, and allowing user-controlled headers that influence routing or body parsing. Adonisjs applications that dynamically construct database queries based on parsed route parameters are especially vulnerable if those parameters originate from a smuggled request.
Cockroachdb-Specific Remediation in Adonisjs — concrete code fixes
To mitigate request smuggling when Adonisjs interacts with Cockroachdb, focus on strict request parsing, explicit route handling, and defensive database access patterns. Below are concrete remediation steps with code examples.
1. Enforce consistent HTTP version and header validation
Configure your Adonisjs server to reject requests with both Content-Length and Transfer-Encoding headers. This prevents smuggling attempts that exploit header parsing differences.
// start/kernel.ts or a custom server hook
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export default class SmugglingValidator {
public async handle(ctx: HttpContextContract, next: () => Promise) {
const request = ctx.request
const hasContentLength = request.hasHeader('content-length')
const hasTransferEncoding = request.hasHeader('transfer-encoding')
if (hasContentLength && hasTransferEncoding) {
ctx.response.status(400).send({ error: 'Invalid headers: Content-Length and Transfer-Encoding cannot both be present' })
return
}
await next()
}
}
Register this handler in the list of server hooks to run before routes are resolved.
2. Use explicit route binding and avoid dynamic endpoint construction
When building routes that execute Cockroachdb queries, use static paths and validate all parameters against an allowlist. Never directly interpolate user input into database statements.
// routes.ts
import Route from '@ioc:Adonis/Core/Route'
Route.get('/api/users/:id', async ({ params, db }) => {
const userId = parseInt(params.id, 10)
if (isNaN(userId)) {
return { error: 'Invalid user ID' }
}
return await db.from('users').where('id', userId).first()
})
3. Parameterize all Cockroachdb queries using the Adonisjs ORM or query builder
Always use parameterized queries to prevent injection and ensure that request parsing inconsistencies cannot alter query intent.
// Example using Adonisjs Lucid ORM
import User from 'App/Models/User'
export default class UsersController {
public async show({ params, response }) {
const user = await User.findOrFail(params.id)
return user
}
}
// Example using raw query builder with placeholders
import { DatabaseService } from '@ioc:Adonis/Addons/Lucid'
export async function getUserById(db: DatabaseService, userId: number) {
const result = await db.from('users').where('id', userId).limit(1)
return result[0]
}
4. Limit trusted proxies and normalize headers early
If you use a reverse proxy, restrict which hosts are allowed to set forwarding headers and normalize headers in a middleware layer before Adonisjs parses the request body.
// middleware/request_normalizer.ts
export default class RequestNormalizer {
public async handle(ctx: HttpContextContract, next: () => Promise) {
const request = ctx.request
// Remove any ambiguous headers that could be exploited for smuggling
if (request.hasHeader('x-forwarded-proto')) {
request.header('x-forwarded-proto', request.header('x-forwarded-proto').split(',')[0].trim())
}
await next()
}
}