Sql Injection in Feathersjs with Api Keys
Sql Injection in Feathersjs with Api Keys — how this specific combination creates or exposes the vulnerability
SQL Injection in a FeathersJS service becomes significantly more dangerous when API keys are used for authentication because keys often grant broad, service-level permissions. FeathersJS services typically rely on data adapters (e.g., feathers-sequelize, feathers-typeorm) that construct queries from user-supplied parameters. If input validation and query building are not strictly enforced, an attacker can inject malicious SQL through parameters that are processed by the adapter.
When API keys are involved, the risk pattern shifts from user-specific data exposure to potential lateral movement or privilege escalation across tenants or tables. For example, an API key with read access to a users table might be abused if a vulnerable query like SELECT * FROM users WHERE id = ${id} is constructed via string concatenation. An attacker could provide an id such as 1 OR 1=1, causing the query to return all rows. Because the request is authenticated with a valid API key, the server may treat the request as legitimate and expose data that should be restricted.
Another scenario involves multi-tenant setups where an API key maps to a specific organization. If a query filters by tenant ID using string interpolation instead of parameterized inputs, an attacker can bypass tenant isolation. A crafted payload such as '; DROP TABLE invoices; -- could be injected if input is not sanitized, leading to data loss or corruption. Even when using an ORM, misconfigured hooks or custom before hooks that modify query parameters can reintroduce SQL fragments from untrusted sources, enabling injection despite the presence of API key checks.
FeathersJS hooks that modify params.sequelize or directly alter the query object are common culprits. For instance, a hook that appends a where clause without validating or parameterizing values can merge attacker-controlled strings into the final SQL. Because API keys are often checked early in the authentication hook, the subsequent service logic may trust the identity context and skip further sanitization, inadvertently allowing injected SQL to execute with the permissions of the API key.
Real-world attack patterns include using SQL Injection to extract sensitive data via UNION-based techniques or to bypass authentication by manipulating WHERE clauses. In a FeathersJS application, an endpoint like /api/invoices that accepts a query parameter filter[where][id] could be exploited if the value is not properly escaped. With a valid API key, an attacker could submit filter[where][id]=1; SELECT * FROM users and, depending on the adapter and database driver, potentially observe unexpected results or errors that leak schema information.
The combination of FeathersJS flexibility and SQL databases requires rigorous input validation, strict use of parameterized queries, and least-privilege database permissions for API keys. Otherwise, authentication via API keys can provide a false sense of security while underlying query construction flaws enable unauthorized data access or manipulation.
Api Keys-Specific Remediation in Feathersjs — concrete code fixes
Remediation centers on ensuring that API key authentication does not inadvertently trust user input. Always treat data from query parameters, headers, and payloads as untrusted, even when a valid API key is present.
1. Use Parameterized Queries with Feathers-Sequelize
When using feathers-sequelize, rely on ORM-safe query structures instead of raw SQL or string concatenation.
// Safe: using parameterized where clause
app.service('invoices').find({
query: {
where: {
id: req.query.id // ORM will parameterize this
}
}
});
// Avoid: raw query with concatenation
app.service('invoices').find({
raw: true,
query: 'SELECT * FROM invoices WHERE id = ' + req.query.id
});
2. Validate and Sanitize Input in Hooks
Add a before hook to validate and sanitize incoming data. Use libraries like joi or express-validator.
const { validator } = require('feathers-hooks-common');
const sanitize = require('validator').escape;
app.service('users').hooks({
before: {
create: [validator({
id: ['isInt'],
email: ['isEmail']
})],
update: [validator({
id: ['isInt']
})]
}
});
3. Enforce Parameterized Filters in Custom Methods
If you expose custom methods that build queries, ensure they use parameterized inputs and avoid dynamic SQL fragments.
// Safe custom method
app.methods.myReport = async function(params) {
const { orgId, dateRange } = params.query;
// Use parameterized query building
return params.sequelize.query(
'SELECT * FROM reports WHERE org_id = :orgId AND date BETWEEN :start AND :end',
{ replacements: { orgId, start: dateRange.start, end: dateRange.end }, type: params.sequelize.QueryTypes.SELECT }
);
};
4. Apply Least-Privilege Database Permissions
Configure database roles so API keys used by Feathers services have only necessary permissions (e.g., SELECT/INSERT on specific tables, no DROP or ALTER). This limits the impact of any potential injection.
5. Disable Raw Query Exposure in Services
Avoid enabling raw: true or exposing endpoints that accept raw SQL fragments. If necessary, strictly whitelist allowed query fields and use parameterized inputs.
Example Secure Service Setup
const app = require('@feathersjs/feathers')();
const sequelize = require('./sequelize-client');
app.configure(require('@feathersjs/express').rest());
app.configure(require('@feathersjs/socketio'));
app.use('/secure-data', require('./services/data-service'));
app.service('secure-data').hooks({
before: {
all: [
async context => {
// Ensure API key validated earlier; now enforce strict query shape
if (context.params.query && context.params.query.filter) {
// Reject unexpected operators or raw expressions
const allowedFields = ['id', 'orgId', 'status'];
const where = context.params.query.filter.where || {};
for (const key of Object.keys(where)) {
if (!allowedFields.includes(key)) {
throw new Error('Invalid filter field');
}
}
}
return context;
}
]
}
});
These steps ensure that API key authentication remains a boundary for identity verification rather than a vector for SQL Injection in FeathersJS applications.
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 |