HIGH prototype pollutionadonisjsmutual tls

Prototype Pollution in Adonisjs with Mutual Tls

Prototype Pollution in Adonisjs with Mutual Tls — how this specific combination creates or exposes the vulnerability

Prototype pollution in AdonisJS can intersect with Mutual TLS (mTLS) in ways that amplify risk when untrusted input reaches server-side object construction or merge routines, even when mTLS provides channel-level authentication. mTLS ensures that both client and server present valid certificates, which strongly authenticates the peer. However, mTLS does not automatically sanitize application-level data. If an AdonisJS application accepts JSON payloads from an authenticated mTLS client and merges those payloads into prototype-based objects (e.g., configuration objects, request context, or models), an attacker who possesses a valid certificate can supply crafted properties that mutate Object.prototype or other shared prototypes.

For example, consider an endpoint that updates user preferences by deeply merging request body with a defaults object. With mTLS, the server may trust the client identity but still process malicious keys like __proto__, constructor.prototype, or constructor.prototype.polluted. Because AdonisJS often uses JavaScript object merging (e.g., via lodash’s merge or manual spread operations), these keys can propagate into shared prototypes if the merge strategy does not explicitly filter or transform them. This can lead to behavior changes across requests, such as injecting falsy guards, altering validation logic, or enabling insecure defaults, which may facilitate further attacks like injection or privilege escalation. The vulnerability is not in mTLS itself but in how the application handles data after mTLS authentication.

Real-world patterns that commonly surface in AdonisJS include using route parameters or body fields to dynamically set object paths (e.g., set(object, path, value)) without restricting key names. If an attacker can control the path and the application merges this into a prototype-rooted structure, they can write to Object.prototype or other global objects. In an mTLS-enabled API, this risk persists because mTLS focuses on peer identity, not input validation. Therefore, even with mTLS enforced, developers must apply strict schema validation and avoid unsafe merging utilities that do not protect against prototype keys.

Mutual Tls-Specific Remediation in Adonisjs — concrete code fixes

Remediation centers on treating mTLS-authenticated inputs as untrusted for object-level operations. In AdonisJS, use strict schema validation (e.g., with ajv or the built-in Validator) and avoid merging untrusted data into shared objects. Below are concrete code examples demonstrating secure practices with mTLS-aware context handling.

Example 1: Enforcing mTLS and validating input before merge

Configure AdonisJS to require client certificates and validate the structure of incoming JSON. Use a schema that explicitly blocks prototype-polluting keys.

// start/hooks.ts or relevant bootstrap file
import { defineConfig } from '@adonisjs/core/app'

export default defineConfig({
  https: {
    enabled: true,
    cert: '/path/to/server-cert.pem',
    key: '/path/to/server-key.pem',
    ca: '/path/to/ca.pem',
    requestCert: true,
    rejectUnauthorized: true, // enforce mTLS
  },
})

// resources/validators/preferences.ts
import { schema } from '@ioc:Adonis/Core/Validator'

export const preferencesSchema = schema.create({
  type: 'object',
  additionalProperties: false,
  properties: {
    theme: schema.string.optional(),
    notifications: schema.boolean.optional(),
  },
  // explicitly reject any unknown keys that could be used for pollution
})

// controllers/PreferencesController.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { preferencesSchema } from 'App/Validators/preferences'

export default class PreferencesController {
  public async update({ request, auth }: HttpContextContract) {
    const clientCert = request.$ssl?.clientCertificate // example mTLS-derived identity
    if (!clientCert) {
      throw new Error('mTLS certificate required')
    }
    const payload = await request.validate({ schema: preferencesSchema })
    // Safe merge: only known keys are retained
    const updated = { ...auth.user!.preferences, ...payload }
    await auth.user!.merge({ preferences: updated }).save()
    return updated
  }
}

Example 2: Secure deep merge utility that filters prototype keys

If merging is necessary, implement a custom merge that omits dangerous keys instead of relying on lodash.merge or spread on potentially polluted objects.

// utils/safeMerge.ts
export function safeMerge(target: Record, source: Record): Record {
  const blockedKeys = new Set(['__proto__', 'constructor', 'prototype'])
  const result = { ...target }
  for (const key of Object.keys(source)) {
    if (blockedKeys.has(key)) {
      continue // drop prototype-polluting keys
    }
    const srcVal = source[key]
    const tgtVal = result[key]
    if (srcVal && typeof srcVal === 'object' && !Array.isArray(srcVal) && tgtVal && typeof tgtVal === 'object') {
      result[key] = safeMerge(tgtVal, srcVal)
    } else {
      result[key] = srcVal
    }
  }
  return result
}

// usage in controller
import { safeMerge } from 'App/Utils/safeMerge'

public async update({ request, auth }: HttpContextContract) {
  const raw = request.only(['theme', 'notifications', '__proto__']) // example input
  const merged = safeMerge(auth.user!.preferences || {}, raw)
  await auth.user!.merge({ preferences: merged }).save()
}

Example 3: mTLS identity binding to prevent unauthorized authenticated abuse

Even with mTLS, correlate certificate metadata with application permissions and avoid using certificate-derived claims to bypass validation.

// middleware/ensureMtlsIdentity.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'

export default async function ensureMtlsIdentity({ request, auth }: HttpContextContract) {
  const cert = request.$ssl?.clientCertificate
  if (!cert) {
    return response.unauthorized({ error: 'Client certificate required' })
  }
  // Example: map certificate fingerprint to user roles, do not trust raw cert fields for object merging
  const fingerprint = cert.fingerprint // hypothetical property
  if (!await isAllowedFingerprint(fingerprint)) {
    throw new Error('Unauthorized mTLS identity')
  }
}

// Apply to routes
Route.resource('preferences', 'PreferencesController')
  .middleware({ before: ['ensureMtlsIdentity'] })

These examples show how to combine mTLS enforcement with disciplined input validation and safe merging to mitigate prototype pollution risks in AdonisJS.

Frequently Asked Questions

Does mTLS prevent prototype pollution in AdonisJS?
No. Mutual TLS authenticates the client and server at the transport layer but does not sanitize application-level input. Prototype pollution risks remain if untrusted data is merged into shared objects, so validation and safe merging are still required.
Can middleBrick detect prototype pollution in AdonisJS APIs secured with mTLS?
middleBrick scans unauthenticated attack surfaces and can identify prototype pollution indicators in OpenAPI specs and runtime behavior. When mTLS is in use, providing the endpoint URL (with appropriate network visibility) allows detection of schema issues and injection-prone merge patterns, though mTLS credentials are not required for the scan.