Sql Injection in Adonisjs with Basic Auth
Sql Injection in Adonisjs with Basic Auth — how this specific combination creates or exposes the vulnerability
SQL Injection in AdonisJS when Basic Auth is used centers on how credentials are handled and how query building interacts with untrusted input. Basic Auth typically supplies a username and password via the Authorization header. If the application uses these credentials to look up a user, developers may construct dynamic queries or use raw conditions that inadvertently incorporate unchecked request inputs, creating injection paths.
Consider an endpoint that authenticates via Basic Auth and then queries a database using values from the request body or query parameters without parameterization. For example, an API might fetch a user’s profile by combining the authenticated identity with an unvalidated id parameter:
const username = auth.getAuthUser().username;
const { id } = request.only(['id']);
const query = `SELECT * FROM users WHERE username = '${username}' AND id = ${id}`;
const result = await Database.query(query);
Here, even though the username comes from Basic Auth, the id value is concatenated directly into the SQL string. An attacker who knows or guesses a valid username can manipulate id to inject SQL, such as supplying id=1 OR 1=1, potentially bypassing intended filters or extracting other rows.
AdonisJS’s ORM (Lucid) encourages parameterized queries, but developers sometimes bypass it in favor of raw query building or string concatenation, especially when mixing authentication context with dynamic filters. A second scenario involves using Basic Auth to identify a tenant or role and then building a conditional clause based on request-supplied sorting or filtering parameters:
const user = await User.findBy('email', auth.getAuthUser().email);
const { sort, status } = request.only(['sort', 'status']);
let query = User.query();
if (status) {
query = query.where('status', status);
}
query = query.orderBy(sort, 'asc'); // sort may be user-controlled
const rows = await query.exec();
While this uses Lucid’s query builder, if sort or status are bound directly without validation, injection can occur via column names or enumerated values. Additionally, if the authentication step is incomplete or misconfigured (for example, relying on a manually parsed header instead of a robust auth provider), the request may fall back to unauthenticated contexts where input validation is less strict, increasing the attack surface.
SSRF and improper input validation can further compound risks in this scenario. If the Basic Auth identity is used to construct URLs or external calls (e.g., fetching user-specific resources), an attacker may inject malicious payloads that lead to internal network probing. Proper sanitization of all user-controlled data, strict schema validation, and avoiding raw SQL concatenation are essential to prevent injection when combining authentication context with dynamic queries.
Basic Auth-Specific Remediation in Adonisjs — concrete code fixes
Remediation focuses on using AdonisJS’s built-in auth utilities and strict input validation to ensure credentials and request data are never interpolated directly into SQL. Always rely on parameterized queries and avoid string-based SQL construction.
1) Use AdonisJS authentication properly with Lucid models and parameterized conditions:
const { auth } = use('Auth');
const request = use('Request');
async function getUserProfile() {
const user = await auth.getUserOrFail();
const { id } = request.get();
// Safe: parameterized where clause using Lucid
const profile = await User.query()
.where('username', user.username)
.andWhere('id', id)
.first();
return profile;
}
This ensures that both the username (from Basic Auth) and the id are passed as bound parameters, eliminating injection risk.
2) Validate and sanitize all incoming fields before using them in query building:
const { sort, status } = request.validate({
type: 'object',
schema: {
sort: 'string',
status: 'string.optional()',
},
});
const user = await auth.getUserOrFail();
const query = User.query()
.where('username', user.username);
if (status) {
query.andWhere('status', status);
}
// Safe ordering: restrict to known columns
const allowedSortColumns = ['id', 'created_at', 'updated_at', 'username'];
const orderBy = allowedSortColumns.includes(sort) ? sort : 'created_at';
query.orderBy(orderBy, 'asc');
const rows = await query.fetch();
By validating sort against a whitelist and using parameterized where clauses, you prevent injection through user-controlled inputs while still allowing flexible queries.
3) When raw SQL is unavoidable, use parameterized bindings explicitly:
const user = await auth.getUserOrFail();
const { customFilter } = request.only(['customFilter']);
const results = await Database.query(
'SELECT * FROM users WHERE username = $1 AND custom_field LIKE $2',
[user.username, `%${customFilter}%`]
);
Even in raw queries, placeholders (e.g., $1, $2 for PostgreSQL) ensure that values are passed safely, preventing injection through concatenation.
These practices align with secure coding guidance and help meet compliance mappings to frameworks such as OWASP API Top 10 and SOC2, without overstating the scanner’s role — middleBrick can detect missing parameterization and weak validation patterns in unauthenticated scans, providing prioritization and remediation guidance to help developers address these issues.
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |