Vulnerable Components in Feathersjs with Api Keys
Vulnerable Components in Feathersjs with Api Keys — how this specific combination creates or exposes the vulnerability
FeathersJS is a framework for real-time applications that commonly uses services and hooks to manage business logic. When API keys are used for authentication, three specific dimensions increase the likelihood of exposure or misuse: the service implementation, hook-level authorization, and client-side request handling. If API keys are accepted in headers or query parameters without strict validation and scope checks, they may be inadvertently exposed in logs, error messages, or through misconfigured service hooks.
In FeathersJS, services are typically defined with a find, get, create, and update methods. If a service does not explicitly validate the presence and correctness of an API key, or if it relies solely on a global hook to enforce authentication, an attacker may exploit gaps in per-method authorization. For example, a service might allow unauthenticated access by default if the hook is misconfigured or bypassed, enabling horizontal or vertical privilege escalation through predictable resource IDs (BOLA/IDOR).
Additionally, API keys stored or transmitted in query strings can leak in server logs, browser history, or referrer headers. FeathersJS applications that echo query parameters in error responses or debug output may unintentionally disclose these keys. The framework’s hook system is powerful, but if developers apply key validation only in a generic before hook and not within service methods, certain administrative or data export endpoints may be left unprotected.
Another risk involves integration with external systems. If a FeathersJS service calls downstream APIs using embedded API keys taken directly from incoming requests without sanitization, it may become vulnerable to Server-Side Request Forgery (SSRF) or insecure consumption patterns. This happens when user-supplied keys are forwarded without validation, potentially exposing internal services or metadata.
Finally, if API keys are accepted via headers such as x-api-key but the service does not enforce strict content-type checks or input validation, malformed or overly long keys may trigger exceptions that reveal stack traces or internal paths. Proper validation, scope scoping, and output sanitization are essential to prevent data exposure through error handling or inventory discovery.
Api Keys-Specific Remediation in Feathersjs — concrete code fixes
To secure API key usage in FeathersJS, validate and scope keys at the service method level, avoid exposing keys in responses, and ensure hooks do not inadvertently bypass checks. Below are concrete examples demonstrating secure patterns.
1. Validate API keys in a service method
Instead of relying only on a global hook, explicitly verify the API key within the service logic. This ensures that even if a hook is misconfigured, the service enforces its own rules.
// services/orders/service.js
class OrdersService {
async find(params) {
const { "x-api-key": apiKey } = params.headers || {};
const validKeys = new Set([process.env.API_KEY_SALES, process.env.API_KEY_REPORTS]);
if (!apiKey || !validKeys.has(apiKey)) {
throw new Error('Not authenticated');
}
const scope = this.getKeyScope(apiKey);
return this.getOrdersForScope(scope, params.query);
}
getKeyScope(apiKey) {
if (apiKey === process.env.API_KEY_SALES) return 'sales';
if (apiKey === process.env.API_KEY_REPORTS) return 'reports';
throw new Error('Invalid key scope');
}
getOrdersForScope(scope, query) {
// scope-aware data retrieval
return db.orders.filter(order => order.scope === scope);
}
}
module.exports = function () {
const app = this;
app.use('/orders', new OrdersService());
};
2. Secure hook-based validation without leaking keys
Use a before hook to check for the presence of a key, but avoid logging it or passing it through to downstream services without masking.
// src/hooks/validate-api-key.js
module.exports = function () {
return async context => {
const { "x-api-key": apiKey } = context.params.headers || {};
const allowedKeys = new Set([process.env.API_KEY_INTERNAL, process.env.API_KEY_EXTERNAL]);
if (!apiKey || !allowedKeys.has(apiKey)) {
throw new Error('Unauthorized');
}
// Do not attach the raw key to context; use a normalized scope instead
context.params.apiScope = apiKey === process.env.API_KEY_INTERNAL ? 'internal' : 'external';
// Remove the key from params to prevent accidental exposure in logs or errors
delete context.params.headers['x-api-key'];
return context;
};
};
// src/app.js
const validateApiKey = require('./hooks/validate-api-key');
app.use('/reports', {
before: {
all: [validateApiKey()],
},
});
3. Avoid exposing keys in error responses and logs
Ensure that error handlers do not include headers or query parameters. Configure FeathersJS to sanitize production errors.
// src/app.js
const { GeneralError } = require('@feathersjs/errors');
app.configure({
errors: {
// In production, do not expose internal details
formatter: (error, hook) => {
if (process.env.NODE_ENV === 'production') {
// Strip headers from error output
const safeError = new GeneralError(error.message);
safeError.code = error.code || 500;
return safeError;
}
return error;
}
}
});
4. Use environment variables and restrict key scope
Store keys in environment variables and map them to specific permissions. Do not concatenate or derive keys in client-side code.
// .env
API_KEY_SALES=sk_live_abc123sales
API_KEY_REPORTS=sk_live_def456reports
// In service, compare against hashed or constant-time values
const crypto = require('crypto');
function safeCompare(input, expected) {
return crypto.timingSafeEqual(Buffer.from(input), Buffer.from(expected));
}
5. Secure client-side usage
When initializing a FeathersJS client, avoid embedding API keys in JavaScript that can be read by browsers. Use short-lived tokens or proxy the key through an authenticated backend.
// client-side: do NOT embed raw API keys
// Instead, rely on session-based auth or proxy through your server
const feathers = require('@feathersjs/client');
const socketio = require('@feathersjs/socketio-client');
const auth = require('@feathersjs/authentication-client');
const app = feathers();
app.configure(auth({
header: 'authentication',
strategy: 'jwt'
}));
app.configure(socketio('https://api.example.com'));
These patterns ensure API keys are handled with least privilege, scoped access, and reduced exposure risk in a FeathersJS application.