HIGH bola idoradonisjsmutual tls

Bola Idor in Adonisjs with Mutual Tls

Bola Idor in Adonisjs with Mutual Tls — how this combination creates or exposes the vulnerability

BOLA (Broken Object Level Authorization) occurs when an API fails to enforce proper access controls at the object level, allowing one user to act on another user’s resources. AdonisJS, a Node.js framework, does not enforce authorization automatically; developers must implement policies and apply them consistently. When mutual TLS (mTLS) is used, the server authenticates the client by validating its certificate, but this does not imply identity mapping to application-level resources. Relying solely on mTLS for authentication can create a false sense of security: once the client is authenticated, the application may authorize requests based on connection attributes (e.g., certificate subject) instead of the actual resource ownership, enabling BOLA.

For example, an mTLS-authenticated endpoint like GET /documents/:id may verify the client certificate but then load a document by ID without checking whether the authenticated principal has permission for that specific document. If IDs are predictable, an attacker can iterate through numeric or UUID identifiers and access documents belonging to other users. This is a classic BOLA/IDOR pattern. The combination of mTLS and missing object-level checks means authentication succeeds, but authorization does not, because the framework does not enforce tenant or ownership scoping at the model layer.

OpenAPI/Swagger analysis with middleBrick can surface this risk when spec definitions expose endpoints with path parameters (e.g., /documents/{id}) but omit security schemes that enforce object ownership. Even when mTLS is declared as a security requirement, the spec may not indicate that each object must be scoped to the authenticated principal’s permissions. Runtime findings from unauthenticated scans (which middleBrick performs) can still detect ID-like parameter exposure and missing authorization checks, highlighting BOLA despite transport-layer mTLS.

Real-world patterns mirror CVE-like scenarios such as insecure direct object references (OWASP API Top 10:2023 API1:2023) where numeric IDs are iterated. With mTLS, attackers may use stolen or spoofed certificates if certificate validation is misconfigured, but the core BOLA issue remains insufficient model-level policy. The framework does not inherently bind the authenticated certificate to a user record and then to data rows; developers must implement this binding explicitly via policies that resolve the certificate to a user ID and scope queries accordingly.

Mutual Tls-Specific Remediation in Adonisjs — concrete code fixes

To remediate BOLA when using mTLS in AdonisJS, couple transport-layer authentication with robust object-level authorization. Map the client certificate to an application identity, enforce ownership checks on every resource access, and avoid trusting path parameters alone. Below are concrete, syntactically correct examples.

1. mTLS setup in AdonisJS (server-side)

Configure the AdonisJS HTTP server to require and validate client certificates. This example uses the built-in HTTPS server with requestCert and rejectUnauthorized.

// start/hooks.ts
import { defineConfig } from '@adonisjs/core/app'
import { join } from 'path'

export default defineConfig({
  https: {
    key: join(__dirname, '../cert/server.key'),
    cert: join(__dirname, '../cert/server.crt'),
    ca: join(__dirname, '../cert/ca.crt'),
    requestCert: 'required',
    rejectUnauthorized: true,
  },
})

2. Certificate-to-user mapping via a custom middleware

Extract the certificate’s subject or serial and resolve it to a user in your application. Store the user ID in the auth context for downstream policies.

// middleware/map_mtls_user.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import User from 'App/Models/User'

export default async function mapMtlsUser({ request, auth, response, next }: HttpContextContract) {
  const cert = request.sslCredentials
  if (!cert || !cert.peerCertificate) {
    return response.unauthorized('Client certificate required')
  }

  // Map certificate fingerprint or subject to a user
  const subject = cert.peerCertificate.subject
  const user = await User.query()
    .where('cert_subject', subject)
    .preload('roles')
    .first()

  if (!user) {
    return response.unauthorized('Certificate not registered')
  }

  // Bind user to auth for policy checks
  await auth.use('api').login(user)
  await next()
}

3. Enforce object-level ownership in a policy

Create an AdonisJS policy that ensures the authenticated user owns the document before allowing access.

// policies/DocumentPolicy.ts
import { schema } from '@ioc:Adonis/Core/Validator'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import Document from 'App/Models/Document'

export default class DocumentPolicy {
  public async view({ params, auth }: HttpContextContract) {
    const document = await Document.findOrFail(params.id)
    // BOLA prevention: scope to authenticated user
    if (document.userId !== auth.user?.id) {
      throw new Error('Unauthorized access to document')
    }
    return document
  }

  public async update({ params, request, auth }: HttpContextContract) {
    const document = await Document.findOrFail(params.id)
    if (document.userId !== auth.user?.id) {
      throw new Error('Unauthorized update')
    }
    // proceed with update
  }
}

4. Apply the policy in a route

Use the policy to gate access to the resource, ensuring that even with mTLS, each operation checks ownership.

// routes.ts
import Route from '@ioc:Adonis/Core/Route'
import DocumentPolicy from 'Policies/DocumentPolicy'

Route.get('/documents/:id', async ({ params, auth }) => {
  const document = await auth.use('api').policy(DocumentPolicy).view(params)
  return document
})

Route.put('/documents/:id', async ({ params, request, auth }) => {
  const document = await auth.use('api').policy(DocumentPolicy).update(params, request.only(['content']))
  return document
})

5. Validate resource existence and scope in controllers

Alternatively or additionally, scope queries directly in controller methods to avoid missing policy coverage.

// controllers/DocumentsController.ts
import Document from 'App/Models/Document'

export default class DocumentsController {
  public async show({ params, auth }: HttpContextContract) {
    const document = await Document.query()
      .where('id', params.id)
      .where('userId', auth.user?.id)
      .firstOrFail()
    return document
  }
}

These steps ensure that mTLS provides client authentication while explicit object-level checks prevent BOLA. middleBrick scans can verify that endpoints with path parameters include appropriate authorization checks and that mTLS is correctly configured in the spec.

Related CWEs: bolaAuthorization

CWE IDNameSeverity
CWE-250Execution with Unnecessary Privileges HIGH
CWE-639Insecure Direct Object Reference CRITICAL
CWE-732Incorrect Permission Assignment HIGH

Frequently Asked Questions

Does mutual TLS alone prevent BOLA in AdonisJS?
No. Mutual TLS authenticates the client at the transport layer but does not enforce object-level authorization. Without explicit checks that map the authenticated certificate to application ownership and scope queries by user ID, BOLA/IDOR vulnerabilities can still exist.
How can middleBrick help detect BOLA when mTLS is used?
middleBrick performs unauthenticated scans that can identify ID-like parameter exposure and missing authorization logic in OpenAPI specs. Even with mTLS declared as a security scheme, the scanner can flag endpoints where path parameters lack binding to authenticated identity and object-level policies, highlighting BOLA risks.