Insecure Direct Object Reference in Adonisjs
How Insecure Direct Object Reference Manifests in Adonisjs
AdonisJS, like any web framework, is susceptible to Insecure Direct Object Reference (IDOR) vulnerabilities when developers fail to properly validate user permissions before accessing resources. In AdonisJS, IDOR typically manifests through route parameter binding and model queries that trust user input without proper authorization checks.
The most common AdonisJS IDOR pattern involves route model binding where the framework automatically fetches a database record based on URL parameters. Consider this vulnerable controller action:
class UserController {
async show({ params, auth }) {
const user = await User.find(params.id) // Vulnerable: no permission check
return user
}
}
Here, any authenticated user can access /users/1, /users/2, etc., regardless of whether they own that user or have permission to view it. The framework's convenience becomes a security liability when authorization is omitted.
Another AdonisJS-specific IDOR scenario occurs with nested relationships. When using with() clauses or eager loading without filtering:
class PostController {
async show({ params }) {
const post = await Post.query()
.where('id', params.id)
.with('comments') // All comments exposed
.first()
return post
}
}
An attacker can exploit this to access comments on posts they don't own, especially if the Comment model has a post_id foreign key that's easily manipulated.
AdonisJS's Lucid ORM can also introduce IDOR through improper use of fetchOrCreate or updateOrCreate methods:
class ProfileController {
async update({ auth, request }) {
const data = request.only(['bio', 'avatar'])
const profile = await Profile.fetchOrCreate(auth.user.id)
await profile.merge(data).save() // Vulnerable if auth.user.id is manipulated
return profile
}
}
While this example shows proper use of auth.user.id, IDOR becomes possible if the code ever uses request parameters instead of authenticated user context for record identification.
AdonisJS route groups and middleware can create IDOR blind spots. Developers might assume middleware protects all routes in a group, but missing middleware declarations on individual routes can expose resources:
Route.group(() => {
Route.get('users/:id', 'UserController.show') // Missing auth middleware
Route.get('posts/:id', 'PostController.show')
}).middleware('auth') // Applied to group, but individual routes might override
The framework's flexibility with route definitions means IDOR can hide in seemingly secure codebases where developers rely on convention over explicit security checks.
AdonisJS-Specific Detection
Detecting IDOR vulnerabilities in AdonisJS applications requires both manual code review and automated scanning. middleBrick's API security scanner specifically identifies AdonisJS IDOR patterns through black-box testing of your endpoints.
For manual detection, search your codebase for these AdonisJS-specific patterns:
// Search for these dangerous patterns:
User.find(params.id)
User.query().where('id', params.id)
await User.find(params.id)
await Post.query().where('id', params.id)
Pay special attention to controllers that handle CRUD operations without explicit authorization checks. In AdonisJS, the can() method from the auth module is your primary defense:
class DocumentController {
async show({ params, auth }) {
const document = await Document.find(params.id)
if (!await auth.user.can('view', document)) {
return response.status(403).send('Forbidden')
}
return document
}
}
middleBrick's scanner tests for IDOR by systematically requesting resource IDs with authenticated sessions, checking if users can access resources they shouldn't own. The scanner's 12 security checks include specific IDOR detection that analyzes your API's response patterns when presented with manipulated identifiers.
For AdonisJS applications, middleBrick examines:
- Route parameter binding without authorization
- Model queries using request parameters directly
- Missing permission checks in controller actions
- Exposure of related resources through eager loading
- Improper use of authenticated user context
The scanner generates an A-F security score with specific findings for each vulnerability category. For IDOR, you'll receive detailed information about which endpoints are vulnerable, the exact attack patterns discovered, and the severity level based on the potential impact.
middleBrick's continuous monitoring (Pro plan) can track IDOR vulnerabilities over time, alerting you when new API endpoints are added that might introduce authorization bypasses. The GitHub Action integration allows you to fail CI/CD builds if IDOR vulnerabilities are detected in staging environments before production deployment.
AdonisJS-Specific Remediation
Remediating IDOR vulnerabilities in AdonisJS requires a combination of proper authorization patterns and defensive coding practices. The framework provides several native features to prevent unauthorized resource access.
First, implement policy-based authorization using AdonisJS's Gate system. Create a app/Policies directory with specific policies for each model:
// app/Policies/UserPolicy.js
class UserPolicy {
async view(user, targetUser) {
return user.id === targetUser.id || await user.can('admin')
}
async update(user, targetUser) {
return user.id === targetUser.id
}
}
// Load in start/kernel.js
const policies = {
User: 'UserPolicy'
}
Then use these policies in your controllers:
class UserController {
async show({ params, auth }) {
const user = await User.find(params.id)
if (!await auth.user.policy('view', user)) {
return response.status(403).send('Forbidden')
}
return user
}
}
For route model binding, AdonisJS allows you to hook into the binding process for authorization:
// app/Listeners/ModelBinding.js
class ModelBinding {
async beforeFind(model, id, { auth }) {
if (model.name === 'User' && auth.user) {
const user = await User.find(id)
if (!await auth.user.can('view', user)) {
throw new Error('Unauthorized access')
}
return user
}
}
}
Another AdonisJS-specific remediation is using the where() clause to filter queries based on the authenticated user:
class PostController {
async index({ auth }) {
const posts = await Post.query()
.where('user_id', auth.user.id)
.withCount('comments')
return posts
}
}
This pattern ensures users only see their own resources without requiring explicit authorization checks in every method.
For nested resources, implement cascading authorization:
class CommentController {
async index({ params, auth }) {
const post = await Post.query()
.where('id', params.post_id)
.where('user_id', auth.user.id)
.with('comments')
.first()
if (!post) {
return response.status(404).send('Not found')
}
return post.comments
}
}
This approach combines data filtering with proper error handling to prevent information leakage through error messages.
middleBrick's remediation guidance for AdonisJS IDOR vulnerabilities includes specific code examples for each finding category, helping you implement the correct authorization patterns for your application's architecture.
Related CWEs: bolaAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-250 | Execution with Unnecessary Privileges | HIGH |
| CWE-639 | Insecure Direct Object Reference | CRITICAL |
| CWE-732 | Incorrect Permission Assignment | HIGH |