Nosql Injection in Adonisjs
How NoSQL Injection Manifests in AdonisJS
AdonisJS, a full-stack TypeScript framework, commonly pairs with MongoDB via its Lucid ORM. NoSQL injection vulnerabilities arise when user-supplied input is improperly integrated into database queries, bypassing intended query logic. In AdonisJS, this frequently occurs in two patterns: direct use of raw query strings with concatenated input, and misuse of Lucid's query builder methods that do not enforce parameterization.
Vulnerable Pattern 1: Raw Query Concatenation
AdonisJS controllers often accept request parameters and pass them to Lucid's whereRaw or executeRaw. If these values are interpolated directly into the query string, an attacker can manipulate the MongoDB query structure. For example:
// Vulnerable AdonisJS controller method
import { schema } from '@ioc:Adonis/Core/Validator'
public async findUsers({ request, response }) {
const email = request.input('email')
// UNSAFE: Direct concatenation into raw query
const users = await User.query().whereRaw(`{ email: '${email}' }`)
return response.json(users)
}An attacker sending [email protected]' } } could close the string and inject additional query operators, potentially matching all documents or exfiltrating data.
Vulnerable Pattern 2: Dynamic Key Names in Query Builder
Lucid's where method expects a column name as the first argument. If this column name is derived from user input without an allow-list, an attacker can target any field in the collection, including internal ones like __v or password:
// Vulnerable: user-controlled field name
public async search({ request }) {
const field = request.input('field') // e.g., 'email', 'role'
const value = request.input('value')
// If 'field' is user-controlled, any collection field can be queried
const results = await User.query().where(field, value)
return results
}While Lucid escapes values, the field name itself is not sanitized. This can lead to unauthorized data access if sensitive fields are exposed.
AdonisJS-Specific Context: AdonisJS's validation layer (VineJS) is often applied to request inputs, but developers may validate only the *value* while ignoring the *field name* or raw query construction. The framework's emphasis on developer experience can inadvertently encourage raw query usage for complex queries, increasing risk if not paired with strict input handling.
AdonisJS-Specific Detection
Detecting NoSQL injection in AdonisJS APIs requires testing the unauthenticated attack surface of endpoints that accept dynamic query parameters. middleBrick's scanner performs this via its Input Validation and Data Exposure checks, sending crafted payloads to identify unsanitized query construction.
Testing Methodology:
middleBrick targets common AdonisJS route patterns (e.g., /api/users?email=test) and injects NoSQL-specific payloads:
- Syntax breakers:
',",}to detect raw query concatenation. - Operator injection:
$ne: ''),$gt: ''to test for operator manipulation. - Field name probing: Non-standard field names like
__proto__orpasswordto identify dynamic key usage.
The scanner analyzes HTTP responses for anomalies: changed HTTP status codes (e.g., 500 vs 200), error messages revealing query structure, or data volume spikes indicating a $ne or empty query match. For AdonisJS applications, it cross-references findings with any provided OpenAPI/Swagger spec to map vulnerable parameters to documented endpoints.
Using middleBrick for Detection:
Scan an AdonisJS API endpoint with the CLI or Dashboard. The resulting report includes a per-category breakdown. A high-severity finding under Input Validation will list the affected endpoint, the payload used (e.g., email=test' }), and evidence like response snippets showing unescaped query errors or unexpected data returns. The scanner does not require credentials, focusing on the unauthenticated surface where such flaws are often exposed.
# Example CLI scan of an AdonisJS API
middlebrick scan https://api.example.com/v1/users
# Output includes JSON with findings, e.g.:
# {
# "score": 65,
# "categories": {
# "input_validation": {
# "severity": "high",
# "findings": [{
# "endpoint": "/v1/users",
# "param": "email",
# "payload": "test' }"
# }]
# }
# }
# }This black-box approach identifies vulnerabilities without needing source code access, critical for AdonisJS apps where deployment configurations may obscure raw query usage.
AdonisJS-Specific Remediation
Remediation in AdonisJS centers on using Lucid's parameterized query methods and strict input validation via VineJS. Never interpolate user input into raw query strings; instead, use placeholders or the query builder's type-safe methods.
Fix 1: Parameterize Raw Queries
If complex queries require raw MongoDB syntax, use Lucid's parameter binding:
import { Database } from '@ioc:Adonis/Lucid/Database'
public async findUsers({ request, response }) {
const email = request.input('email')
// SAFE: Parameterized raw query
const users = await Database.raw(`SELECT * FROM users WHERE email = ?`, [email])
// For MongoDB via Lucid, use .whereRaw with bindings:
// await User.query().whereRaw('{ email: ? }', [email])
return response.json(users)
}This ensures the input is treated as data, not query syntax. Note: AdonisJS Lucid's whereRaw supports second-argument bindings for MongoDB driver compatibility.
Fix 2: Validate and Sanitize Field Names
When accepting dynamic field names, use an allow-list:
import { schema, rules } from '@ioc:Adonis/Core/Validator'
public async search({ request }) {
const allowedFields = ['email', 'name', 'role']
const fieldSchema = schema.string({
exists: true,
allowed: allowedFields // VineJS allow-list
})
const validated = await request.validate({
schema: {
field: fieldSchema,
value: schema.string()
}
})
const results = await User.query().where(validated.field, validated.value)
return results
}Fix 3: Prefer Query Builder Over Raw Queries
Lucid's fluent query builder automatically escapes values and is less error-prone:
// SAFE: Fluent builder with dynamic values
public async filterUsers({ request }) {
const { role, active } = request.only(['role', 'active'])
const query = User.query()
if (role) query.where('role', role)
if (active !== undefined) query.where('active', active === 'true')
return query
}Additional AdonisJS-Specific Steps:
- Enable AdonisJS's built-in Content Security Policy (CSP) to mitigate client-side injection that could chain with NoSQL flaws.
- Use
@ioc:Adonis/Addons/Validatorto enforce type constraints on all API inputs, not just values but also field names when dynamic. - Review any use of
Database.connection.executeorcollection.raw()for direct MongoDB command execution; replace with parameterized equivalents.
Frequently Asked Questions
Does AdonisJS's Lucid ORM automatically prevent NoSQL injection?
.where), but it does not sanitize dynamic field names or protect raw query concatenation. Developers must still validate inputs and avoid interpolating user data into raw query strings. Vulnerabilities arise from misuse, not Lucid itself.How does middleBrick detect NoSQL injection in an AdonisJS API without source code?
' }, $ne: '') to API endpoints and analyzing responses for query errors, data leakage, or status changes. It identifies vulnerable parameters by observing behavioral differences between benign and malicious requests, then maps findings to OWASP API Top 10 categories like Input Validation.