Nosql Injection in Adonisjs
How Nosql Injection Manifests in Adonisjs
Nosql injection in Adonisjs applications typically occurs when user-supplied data is directly interpolated into MongoDB queries without proper sanitization. Unlike SQL injection, Nosql injection exploits the query language's ability to accept JavaScript objects and operators, allowing attackers to manipulate query logic.
Consider this common Adonisjs pattern:
const User = use('App/Models/User')
async show({ params }) {
const user = await User.find(params.id)
return user
}This appears safe, but if params.id contains { $ne: null }, the query becomes:
{ _id: { $ne: null } }This returns all users instead of one specific user—a classic BOLA (Broken Object Level Authorization) scenario enabled by Nosql injection.
More dangerous patterns include:
async search({ request }) {
const query = request.input('query', {})
return await User.where(query).fetch()
}An attacker can send { "username": { "$ne": null }, "password": { "$ne": null } } to bypass authentication entirely. The where method accepts raw JavaScript objects, making it vulnerable when user input isn't validated.
Adonisjs's Lucid ORM (built on top of Mongoose-like query builders) provides several injection vectors:
where()with object parametersorWhere()with dynamic conditionsorderBy()with unsanitized field nameslimit()andskip()with numeric injection
The framework's flexibility becomes a liability when developers assume all input is safe. Nosql injection can also occur through:
async advancedSearch({ request }) {
const filters = request.input('filters', {})
const query = User.query()
if (filters.age) query.where('age', filters.age)
if (filters.name) query.where('name', filters.name)
return await query.fetch()
}Here, if filters.age contains { $gt: 0, $lt: 120, $ne: 25 }, the attacker can craft complex queries that return unintended data sets.
Adonisjs-Specific Detection
Detecting Nosql injection in Adonisjs requires examining both code patterns and runtime behavior. The most effective approach combines static analysis with dynamic scanning.
Code-level indicators include:
const User = use('App/Models/User')
async vulnerableMethod({ request }) {
const unsafeInput = request.input('data')
return await User
.where('field', unsafeInput) // Vulnerable
.fetch()
}Safe patterns use explicit validation:
async secureMethod({ request }) {
const validated = request.only(['username', 'email'])
return await User
.where('username', validated.username)
.where('email', validated.email)
.fetch()
}middleBrick's scanning specifically targets Adonisjs applications by:
- Analyzing route handlers for direct query parameter usage
- Checking for unsafe
request.input()calls without validation - Examining model query construction patterns
- Testing for BOLA vulnerabilities through IDOR-style attacks
The scanner sends crafted payloads like { $where: "this.password.length > 0" } and { $ne: null } to test query manipulation. For Adonisjs specifically, it checks:
GET /api/users?id[$ne]=null
GET /api/users?filter[username][$exists]=true
GET /api/users?sort[$meta]=textScoremiddleBrick's LLM security module also detects if your Adonisjs API serves AI endpoints vulnerable to prompt injection, which often co-exists with Nosql injection vulnerabilities in modern applications.
Runtime detection involves monitoring for:
- Unexpected query performance (indicating full collection scans)
- Database logs showing unusual query patterns
- Authentication bypass attempts
Adonisjs-Specific Remediation
Remediating Nosql injection in Adonisjs requires a defense-in-depth approach using the framework's built-in features.
First, always validate and sanitize input using Adonisjs's validation system:
const { validate } = use('Validator')
async secureShow({ params }) {
const rules = { id: 'required|objectid' }
const validation = await validate(params, rules)
if (validation.fails()) {
return response.status(400).send('Invalid ID format')
}
return await User.find(params.id)
}The objectid rule ensures only valid MongoDB ObjectIDs pass through, blocking injection attempts.
For query building, use explicit whitelisting:
async search({ request }) {
const allowedFields = ['username', 'email', 'age']
const filters = request.only(allowedFields)
const query = User.query()
Object.keys(filters).forEach(field => {
query.where(field, filters[field])
})
return await query.fetch()
}This prevents attackers from injecting arbitrary operators by restricting fields to a known safe list.
For complex queries, use parameterized conditions:
async advancedSearch({ request }) {
const { ageMin, ageMax } = request.only(['ageMin', 'ageMax'])
const query = User.query()
if (ageMin !== undefined) {
query.where('age', '>=', parseInt(ageMin))
}
if (ageMax !== undefined) {
query.where('age', '<=', parseInt(ageMax))
}
return await query.fetch()
}Adonisjs's Lucid ORM provides additional safety through model scopes:
class User extends Model {
static scopeAllowedSearch(query, filters) {
const allowed = ['username', 'email']
Object.keys(filters)
.filter(field => allowed.includes(field))
.forEach(field => {
query.where(field, filters[field])
})
return query
}
}
// Controller usage
async search({ request }) {
const filters = request.only(['username', 'email'])
return await User
.allowedSearch(filters)
.fetch()
}For AI/ML endpoints in Adonisjs, implement output filtering:
async generateResponse({ request }) {
const prompt = request.input('prompt', '')
const response = await aiModel.generate(prompt)
// Remove sensitive content
const sanitized = response
.replace(/API_KEY_[A-Z0-9]+/g, '[REDACTED]')
.replace(/password|secret|token/gi, '[REDACTED]')
return sanitized
}