HIGH bola idoradonisjsbasic auth

Bola Idor in Adonisjs with Basic Auth

Bola Idor in Adonisjs with Basic Auth — how this specific combination creates or exposes the vulnerability

Broken Object Level Authorization (BOLA) occurs when an API fails to enforce authorization checks between a user and the specific object they are attempting to access. In AdonisJS, this commonly arises when route parameters such as :id are used to locate a record without verifying that the authenticated user owns or is permitted to interact with that record. When Basic Auth is used, the framework validates credentials and attaches a user identity to the request, but it does not automatically enforce object ownership. If route handlers then directly use request parameters to query models (e.g., Profile.findOrFail(request.param('id'))), an attacker can manipulate the ID to access or modify objects belonging to other users.

Basic Auth transmits credentials in an encoded (not encrypted) format per request. While middleBrick checks for the presence of Basic Auth and flags unauthenticated endpoints, it also examines whether authorization checks align with authentication. In AdonisJS applications, a typical vulnerability pattern is to authenticate via Basic Auth and then load objects without scoping to the authenticated user’s identity. For example, an endpoint like GET /api/profiles/:id that uses Profile.findOrFail(params.id) after basicAuth validation is vulnerable because any authenticated user can increment or guess numeric IDs to view or alter other profiles. This becomes critical when sensitive operations (such as updating email or role flags) lack a user-to-object ownership verification step.

AdonisJS provides authentication utilities and guards, but developers must explicitly scope queries. A vulnerable implementation might look like:

import Profile from 'App/Models/Profile'

Route.get('/profiles/:id', async ({ request, auth }) => {
  const profile = await Profile.findOrFail(request.param('id'))
  return profile
})

Here, auth is used to confirm the presence of Basic Auth, but the handler does not verify that the authenticated user’s ID matches the profile being requested. middleBrick’s checks for BOLA/IDOR alongside authentication would flag this as a high-severity finding because the endpoint exposes object-level authorization weaknesses despite being protected by Basic Auth.

Real-world attack patterns include enumeration of numeric IDs, exploitation of predictable UUIDs, or manipulation of foreign keys to access administrative or private records. Since middleBrick tests unauthenticated attack surfaces where applicable and evaluates authenticated surfaces for authorization gaps, it can surface these BOLA risks when Basic Auth is present but object checks are missing.

Basic Auth-Specific Remediation in Adonisjs — concrete code fixes

Remediation centers on scoping every data access to the authenticated user’s identity. In AdonisJS, after validating Basic Auth, always join or filter records by the authenticated user’s primary key. Avoid trusting route parameters for ownership decisions. Below are concrete, safe patterns.

1. Scope queries to the authenticated user

Instead of loading an object by ID alone, include the user identifier in the query. If using AdonisJS Lucid models with a user_id foreign key:

import Profile from 'App/Models/Profile'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'

export default class ProfilesController {
  public async show({ request, auth }: HttpContextContract) {
    const user = await auth.authenticate()
    const profile = await Profile.query()
      .where('user_id', user.id)
      .preload('user')
      .where('id', request.param('id'))
      .firstOrFail()
    return profile
  }
}

This ensures that even if an attacker guesses or increments the id, they cannot access profiles where user_id does not match the authenticated user. middleBrick’s findings for BOLA/IDOR would be cleared because the query includes a user-level filter.

2. Use route binding with ownership checks

AdonisJS route binding can be combined with authorization logic. Define a model binding, then verify ownership before proceeding:

import Route from '@ioc:Adonis/Core/Route'
import Profile from 'App/Models/Profile'

Route.resource('profiles', 'Profile').apiOnly()

// In ProfileController show method
public async show({ params, auth }: HttpContextContract) {
  const user = await auth.authenticate()
  const profile = await Profile.findOrFail(params.id)

  if (profile.userId !== user.id) {
    throw response.unauthorized('Access denied to this profile')
  }

  return profile
}

This pattern explicitly compares the loaded object’s user identifier with the authenticated user’s ID, closing the BOLA gap. Note that Basic Auth ensures a user is authenticated, but explicit comparison is still required.

3. Avoid exposing internal IDs in URLs when possible

While not always practical, using opaque identifiers (e.g., hashids or UUIDs) reduces direct enumeration risk. Combine with ownership checks:

import Route from '@ioc:Adonis/Core/Route'
import Profile from 'App/Models/Profile'
import { Hashids } from 'hashids'

const hashids = new Hashids('your-salt', 8)

Route.get('/profiles/:publicId', async ({ params, auth }) => {
  const user = await auth.authenticate()
  const id = hashids.decode(params.publicId)[0]
  if (!id) throw response.badRequest('Invalid public identifier')

  const profile = await Profile.query()
    .where('user_id', user.id)
    .where('id', id)
    .firstOrFail()

  return profile
})

This adds a layer of obscurity and ensures that even if public IDs are guessed, they are scoped to the authenticated user. middleBrick’s checks for BFLA/Privilege Escalation and Property Authorization would validate that such scoping is consistently applied across endpoints.

4. Centralize authorization logic

For larger applications, use policies or services to enforce object ownership, reducing duplication and errors:

import Profile from 'App/Models/Profile'

class ProfilePolicy {
  public async authorizeView(user: User, id: number) {
    const count = await Profile.query()
      .where('user_id', user.id)
      .where('id', id)
      .count('* as total')
    return count[0].total > 0
  }
}

// In controller
public async update({ params, auth }: HttpContextContract) {
  const user = await auth.authenticate()
  const canUpdate = await new ProfilePolicy().authorizeView(user, params.id)
  if (!canUpdate) throw response.forbidden('Unauthorized')
  // proceed with update
}

By centralizing checks, you align authentication (via Basic Auth) with robust, maintainable authorization that mitigates BOLA risks.

FAQ

  • Does Basic Auth alone prevent BOLA in AdonisJS?

    No. Basic Auth confirms identity but does not enforce object-level permissions. Developers must scope queries to the authenticated user to prevent BOLA.

  • How does middleBrick evaluate Basic Auth and BOLA together?

    middleBrick tests authenticated endpoints for authorization gaps by correlating authentication findings with BOLA/IDOR checks. It examines whether route parameters are safely scoped to the authenticated identity, surfacing high-severity findings when ownership checks are missing despite Basic Auth being present.

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 Basic Auth alone prevent BOLA in AdonisJS?
No. Basic Auth confirms identity but does not enforce object-level permissions. Developers must scope queries to the authenticated user to prevent BOLA.
How does middleBrick evaluate Basic Auth and BOLA together?
middleBrick tests authenticated endpoints for authorization gaps by correlating authentication findings with BOLA/IDOR checks. It examines whether route parameters are safely scoped to the authenticated identity, surfacing high-severity findings when ownership checks are missing despite Basic Auth being present.