Nosql Injection in Adonisjs with Api Keys
Nosql Injection in Adonisjs with Api Keys — how this specific combination creates or exposes the vulnerability
Nosql Injection occurs when user-controlled input is directly used to build query conditions for a NoSQL database, allowing an attacker to manipulate query logic. In AdonisJS, this commonly affects endpoints that rely on the Lucid ORM to construct queries from request parameters. When API keys are used for authentication but are not strictly validated or are accepted as query parameters, they can become part of the data flow that reaches the database layer.
Consider an endpoint that filters records by tenantId derived from an API key. If the API key is extracted from headers but then concatenated into a dynamic query object without strict validation, an attacker who can guess or brute-force a key might influence the query structure. For example, an API key passed as a query parameter like GET /data?api_key=abc123 could be bound into a JavaScript object used in a merge call:
const apiKey = request.query().api_key
const data = await Model.query()
.where('api_key', apiKey)
.merge({ 'status.verified': true })
.exec()
If api_key is user-supplied and not strictly validated against a known set, an attacker could inject operators such as $where, $ne, or nested logical conditions depending on the driver’s parsing behavior. In MongoDB bindings used indirectly through AdonisJS services, a payload like { api_key: { $ne: null } } could unintentionally expand the scope of returned rows. Similarly, with JSON columns, an attacker might supply { "profile.$[elem]": { "$exists": true } } to probe document structures.
The risk is amplified when the API key is also used to derive dynamic model scopes or soft-deasure checks. If the key influences which rows are visible via a scope applied through Model.query().applyScopes(), injection can bypass intended visibility rules, leading to IDOR-like behavior across tenants. AdonisJS does not inherently sanitize NoSQL operators from user input, so any input that eventually feeds into query-building methods must be treated as untrusted.
LLM/AI Security checks in middleBrick highlight these patterns by detecting unsafe consumption of user input into data-store interactions and flagging missing validation on identifiers that may reach the database. This is especially important when API keys are treated as data rather than strictly as authentication metadata.
Api Keys-Specific Remediation in Adonisjs — concrete code fixes
Remediation focuses on strict validation, avoiding direct concatenation of API key values into query objects, and isolating authentication metadata from query construction.
- Validate API keys against a fixed set or database record before using them in queries. Do not forward raw user input into database conditions.
- Use parameterized scoping instead of dynamic property merging when possible.
- Treat API keys as authentication tokens only; keep them out of query filters and URL paths that influence data selection.
Secure example using explicit validation and parameter-based scoping:
// Assume ApiKey is a Lucid model storing authorized keys
const apiKeyModel = await ApiKey.findBy('key', request.header('X-API-Key'))
if (!apiKeyModel) {
throw new Response.BadRequestException('Invalid API key')
}
// Use a static scope defined on the model instead of merging dynamic user input
const data = await Model.query()
.applyScopes(['forTenant', 'active'])
.where('tenant_key', apiKeyModel.tenant)
.exec()
// Define scopes in the model
// models/Model.js
class Model extends BaseModel {
static scopes = {
forTenant(builder) {
return builder.where('tenant_key', currentTenant)
},
active(builder) {
return builder.where('status', 'active')
}
}
}
If you must filter by a user-provided key, normalize and sanitize it strictly:
const rawKey = request.input('key', '')
// Allow only alphanumeric keys of fixed length
if (!/^[a-f0-9]{32}$/i.test(rawKey)) {
throw new ValidationException('Invalid key format')
}
// Use parameterized queries, not string interpolation
const records = await Model.query()
.where('api_key', rawKey)
.andWhere('deleted_at', null)
.exec()
In the GitHub Action, configure quality gates so that scans failing due to unsafe data flows or missing validation block merges. In the Web Dashboard, track changes over time to ensure that new endpoints do not reintroduce these patterns. The MCP Server can surface these findings directly in AI coding assistants when you are writing query logic.