Idor Enumeration in Adonisjs
How Idor Enumeration Manifests in Adonisjs
In Adonisjs applications, IDOR enumeration commonly occurs when developers rely on sequential numeric IDs in route parameters without implementing proper authorization checks. A typical pattern involves resource endpoints like GET /users/:id or GET /orders/:id where the :id parameter is used directly to fetch database records without verifying if the authenticated user owns or is permitted to access that resource.
Adonisjs-specific manifestations include:
- Controllers using
param('id')from the HttpContext to query models viaUser.find()orOrder.find()without ownership validation - Route definitions in
start/routes.tsthat expose numeric IDs (e.g.,Route.get('users/:id', 'UsersController.show')) lacking middleware for authorization - Use of Adonisjs Lucid ORM's
find()orfirst()methods that return records based solely on ID, bypassing relational checks - Pagination or listing endpoints where
limitandoffsetparameters are manipulated to enumerate through ID ranges (e.g.,/api/posts?limit=1&offset=1000)
Attackers enumerate IDs by incrementing or decrementing values (e.g., trying /api/invoices/1, /api/invoices/2, etc.) to access unauthorized data. This is particularly effective in Adonisjs apps using auto-incrementing primary keys, which is the default in Lucid migrations. Real-world analogues include CVE-2020-13942 (IDOR in a healthcare API) and OWASP API1:2023 Broken Object Level Authorization.
Adonisjs-Specific Detection
Detecting IDOR enumeration in Adonisjs requires examining both route exposure and controller logic. middleBrick identifies this through black-box testing of the unauthenticated or low-privilege attack surface by:
- Probing endpoints with numeric ID parameters (e.g.,
/api/v1/users/1,/api/v1/users/2) and comparing responses for sensitive data leakage - Detecting patterns where sequential ID access returns 200 OK with PII, financial data, or proprietary information without corresponding authentication challenges
- Analyzing OpenAPI/Swagger specs (if present) to identify routes with
:idpath parameters lacking security schemas or authorization requirements - Testing for enumeration via parameter manipulation in query strings (e.g.,
?user_id=1) or headers that Adonisjs controllers might inadvertently use
For example, if an Adonisjs route GET /api/v1/profile/:id returns a user's email and phone number when accessed with id=5 by an unauthenticated user, middleBrick flags this as a high-severity IDOR finding. The scanner cross-references runtime responses with spec definitions to confirm whether the endpoint should be restricted. Detection focuses on behavior: if accessing id=N+1 returns data belonging to another user, it confirms enumeration viability. middleBrick does not assume framework internals; it observes responses to crafted requests, making it effective against custom Adonisjs implementations.
Adonisjs-Specific Remediation
Remediating IDOR enumeration in Adonisjs involves enforcing authorization at the controller or middleware level using Adonisjs's built-in features. Key strategies include:
- Using Adonisjs middleware to verify resource ownership before proceeding
- Implementing policy checks with Adonisjs's authorizer (via
@ioc:Adonis/Addons/Bouncer) or custom guards - Avoiding direct use of
param('id')for access control; instead, scope queries to the authenticated user's context
Example: Fixing a vulnerable UsersController.show method
// app/Controllers/Http/UsersController.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import User from 'App/Models/User'
public async show({ params, auth, response }: HttpContextContract) {
// Remediation 1: Use authenticated user's ID instead of params.id
const user = await User.findOrFail(auth.user?.id)
return response.ok(user)
}
For cases where resource access depends on the ID parameter (e.g., admin viewing any user):
// app/Controllers/Http/UsersController.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import User from 'App/Models/User'
public async show({ params, auth, bouncer, response }: HttpContextContract) {
// Remediation 2: Authorize using Bouncer before fetching
await bouncer.with('UserPolicy').authorize('view', auth.user)
const user = await User.findOrFail(params.id)
return response.ok(user)
}
Define the policy in start/policy.ts:
// start/policy.ts
import { BouncerContract } from '@ioc:Adonis/Addons/Bouncer'
import User from 'App/Models/User'
const userPolicy = bouncer => {
bouncer.define('UserPolicy', (user, targetUser) => {
// Allow if user is admin OR viewing own profile
return user.isAdmin || user.id === targetUser.id
})
}
export default { userPolicy }
Alternative: Scope the query to prevent enumeration even if ID is guessed
// app/Controllers/Http/OrdersController.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import Order from 'App/Models/Order'
public async index({ auth, request, response }: HttpContextContract) {
// Remediation 3: Never expose raw IDs; scope to user
const page = request.input('page', 1)
const limit = request.input('limit', 10)
const orders = await Order.query()
.where('user_id', auth.user?.id)
.paginate(page, limit)
return response.ok(orders)
}
These approaches leverage Adonisjs's native authentication, authorization, and ORM capabilities to ensure users only access permitted resources, eliminating IDOR enumeration vectors.