Out Of Bounds Write in Adonisjs with Mutual Tls
Out Of Bounds Write in Adonisjs with Mutual Tls
An Out Of Bounds Write occurs when code writes data past the boundaries of a buffer or data structure, which can corrupt memory or cause undefined behavior. In AdonisJS, this risk can surface when handling raw buffers, streams, or typed arrays, especially when requests are authenticated via Mutual TLS (mTLS). With mTLS, the server validates the client certificate before processing the request. If the application then reads the client certificate or request body into a fixed-size buffer without proper length checks, an attacker can supply a crafted payload that triggers an out-of-bounds write.
Consider a scenario where an AdonisJS route processes a client certificate’s Common Name (CN) or a custom header and copies it into a fixed-length buffer. Without validating the length, an oversized value can overflow into adjacent memory. Because mTLS terminates the TLS layer before the application sees the request, developers may assume the transport is inherently safe and skip input validation. This false sense of security can lead to unchecked copying of certificate fields or uploaded file chunks, creating an out-of-bounds condition.
Real-world examples include using Node.js Buffer operations that do not enforce boundaries or passing unchecked data to native addons. An attacker could exploit this to corrupt the process heap, potentially leading to arbitrary code execution or denial of service. The vulnerability is not in mTLS itself but in how the application handles data after the TLS layer has authenticated the client.
middleBrick scans can detect unsafe handling of buffers and certificate metadata by correlating OpenAPI specifications with runtime behavior, identifying endpoints that accept large or untrusted payloads without adequate validation. This is especially important for mTLS-enabled APIs where unauthenticated scanning endpoints still expose the application surface once the TLS handshake completes.
Mutual Tls-Specific Remediation in Adonisjs
Remediation focuses on validating all inputs derived from the mTLS context and applying strict bounds checking before any write operation. Treat certificate fields and headers as untrusted data.
1. Validate certificate fields before use
Do not assume the CN or SAN lengths are small. Explicitly check length and sanitize before copying into fixed-size structures.
import { schema } from '@ioc:Adonis/Core/Validator'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export default class CertificatesController {
public async store({ request, response }: HttpContextContract) {
const clientCert = request.$ssl?.clientCert
if (!clientCert) {
return response.badRequest({ error: 'Client certificate required' })
}
// Validate CN length before using it
const cn = clientCert.subject.commonName
const safeCn = this.sanitizeCommonName(cn)
// Use safeCn in business logic instead of raw cn
return response.ok({ commonName: safeCn })
}
private sanitizeCommonName(cn?: string): string {
if (!cn) return ''
// Limit to a reasonable length, e.g., 256 characters
return cn.length > 256 ? cn.substring(0, 256) : cn
}
}
2. Use constant-time comparison for sensitive values
When comparing certificate fingerprints or secrets, use a constant-time comparison to avoid timing-based side channels that could aid an attacker in crafting out-of-bounds inputs.
import { timingSafeEqual } from 'crypto'
export function safeBufferCompare(a: Buffer, b: Buffer): boolean {
if (a.length !== b.length) {
return false
}
// timingSafeEqual requires buffers of the same length
return timingSafeEqual(a, b)
}
3. Sanitize uploaded file buffers
If your mTLS API accepts file uploads, validate the buffer size and content type before processing. Do not write directly to fixed-size buffers.
import { schema } from '@ioc:Adonis/Core/Validator'
const fileSchema = schema.create({
file: schema.file.optional({}, {
size: '4mb',
extnames: ['jpg', 'png', 'pdf']
})
})
export const validateFile = async (ctx: HttpContextContract) => {
await fileSchema.validateOrThrow(ctx)
const file = ctx.request.file('file')
if (file) {
const buffer = await file.toBuffer()
// Process buffer safely; do not assume size is bounded by extnames alone
if (buffer.length > 0 && buffer.length <= 4 * 1024 * 1024) {
// Safe processing
}
}
}
4. Enforce strict schema for JSON payloads
Even with mTLS, JSON payloads must be validated against a strict schema to prevent oversized strings that could lead to out-of-bounds conditions when used internally.
import { schema } from '@ioc:Adonis/Core/Validator'
const payloadSchema = schema.create({
metadata: schema.object({
description: schema.string.optional({}, { trim: true, escape: false }),
tags: schema.array.optional(schema.string.optional({}, { trim: true }))
})
})
export const validatePayload = async (ctx: HttpContextContract) => {
await payloadSchema.validateOrThrow(ctx)
// Safe to use validated payload
}