Injection Flaws in Feathersjs with Api Keys
Injection Flaws in Feathersjs with Api Keys — how this specific combination creates or exposes the vulnerability
Injection flaws in FeathersJS when API keys are involved typically arise from insufficient validation of inputs that reach service logic and from misconfigured transports that expose key-sensitive endpoints. FeathersJS is a framework for real-time applications that relies heavily on hooks and services; if user-supplied data is directly used to build queries or passed into transport-specific handling without sanitization, attackers can inject malicious payloads through parameters that the service exposes.
Consider a service that accepts query parameters such as token or api_key to filter results. If these values are concatenated into a database query or used to dynamically select fields without strict allowlisting, an attacker may leverage injection techniques (e.g., NoSQL injection or command injection depending on the backend) to bypass intended filters. For example, a malformed query like {'$where': 'this.apiKey === "' + userSuppliedKey + '"'} could manipulate the execution context if the key is not validated.
Moreover, if API keys are accepted via headers or URL parameters and then forwarded to external systems or logged in detail, this increases the risk of injection through log injection or header smuggling. In a FeathersJS application, services often rely on hooks that merge params into queries; if an API key is placed in params.query without sanitization and the service uses that query to interact with a database, the key value itself becomes a vector for injection.
Another scenario involves unauthenticated LLM endpoints or external integrations where API keys are passed as part of requests. If these keys are reflected in error messages or used in dynamic evaluation (for example, constructing service URLs from user input), attackers may exploit SSRF or command injection to exfiltrate keys or pivot within the network. The interplay between FeathersJS service flexibility and key handling can unintentionally broaden the attack surface if input validation and output encoding are not consistently applied.
Finally, when using OpenAPI specs with $ref resolution, if the spec describes API key security schemes but runtime services do not enforce strict validation of key formats and origins, scanning can detect mismatches between declared protections and actual behavior. This discrepancy can lead to authorization bypass or injection-style abuse where keys are treated as data rather as strict credentials.
Api Keys-Specific Remediation in Feathersjs — concrete code fixes
Remediation focuses on strict validation, avoiding direct concatenation of key-like values into executable logic, and ensuring that API keys are treated as opaque credentials rather than queryable data. Below are concrete code examples for secure FeathersJS service configuration and hooks.
1. Validate API key format early in a hook
Use a before hook to validate the presence and format of API keys, rejecting malformed input before it reaches services.
// src/hooks/validate-api-key.js
module.exports = function () {
return async context => {
const { apikey } = context.params.query || {};
if (typeof apikey !== 'string') {
throw new Error('Invalid API key');
}
// Allowlist expected format: 32 hex characters
if (!/^[a-f0-9]{32}$/i.test(apikey)) {
throw new Error('Invalid API key format');
}
// Remove raw key from query to avoid accidental exposure
delete context.params.query.apikey;
return context;
};
};
2. Use parameterized queries instead of dynamic key-based filtering
Instead of injecting key values into query objects, map keys to predefined permissions or service accounts server-side.
// src/services/orders/orders.class.js
const { ServiceBase } = require('feathers-sequelize');
class SecureOrdersService extends ServiceBase {
async find(params) {
const { apikey } = params.authenticated; // Assume key resolved earlier
// Use the key to look up permissions, not to filter inline
const permissions = await this.app.service('api-permissions').get(apikey);
return super.find({
...params,
paginate: false,
where: {
scope: permissions.scope,
// other safe filters
}
});
}
}
3. Configure transports to avoid key leakage in URLs and logs
Ensure API keys are not passed in query strings for WebSocket or REST transports. Use headers only over TLS and avoid logging key values.
// src/app.js
const feathers = require('@feathersjs/feathers');
const rest = require('@feathersjs/express').rest;
const socketio = require('@feathersjs/socketio');
const validateApiKey = require('./hooks/validate-api-key');
const app = feathers();
app.configure(rest());
app.configure(socketio());
// Use a hook to extract and validate keys from headers only
app.hooks({
before: {
all: [validateApiKey()]
}
});
// Do not append API keys to URLs in service calls
app.use('/data', require('./services/data'));
4. Avoid reflection and external forwarding of raw keys
Do not echo API keys in responses or error messages. When integrating with external services, use server-side mappings instead of forwarding raw keys from client-supplied values.
// BAD: potential reflection
app.service('external').hooks({
before: {
create: async context => {
// DO NOT do this:
// context.data.url += `?key=${context.params.query.apikey}`;
// Instead:
const token = await vault.lookup(context.params.authenticated.scope);
context.data.headers = { Authorization: `Bearer ${token}` };
return context;
}
}
});
5. Align OpenAPI spec security schemes with runtime enforcement
Ensure that your spec accurately describes key locations and that runtime hooks enforce those requirements, reducing the gap that scanning might detect between spec and implementation.
// openapi.yaml excerpt
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: X-API-Key