Insecure Design in Feathersjs with Api Keys
Insecure Design in Feathersjs with Api Keys — how this specific combination creates or exposes the vulnerability
Insecure design in a FeathersJS service occurs when authorization checks are missing or applied inconsistently, and this risk is heightened when the service relies on API keys without validating scope, ownership, or context. FeathersJS is a framework that encourages a service-first architecture; each service can define its own hooks and logic. If a service exposes endpoints that accept API keys but does not enforce per-request authorization tying the key to a specific resource or action, an Insecure Design flaw emerges.
Consider a FeathersJS service for a multi-tenant analytics platform where each organization is issued an API key. A developer might configure the service to authenticate requests via an API key hook but omit checks that ensure the key grants access only to the organization’s own data. Because FeathersJS allows hooks to run globally or per-service, an insecure design might place the key validation in a global before hook but skip granular authorization within the find or get methods. This creates a situation where a caller presenting a valid API key can potentially iterate over resource IDs (e.g., /reports/1, /reports/2) and access data belonging to other organizations, a Broken Level of Authorization (BOLA) / Insecure Direct Object Reference (IDOR) pattern enabled by a flawed design rather than a missing authentication mechanism.
The vulnerability is further exposed when API keys are passed in headers or query parameters without additional context checks. For example, if a service uses a single API key for read-only operations but the same key is accepted for write actions without verifying intent or rate, the design fails to enforce principle of least privilege. An attacker could capture a key from logs or network traffic and misuse it across endpoints because the service does not validate referer context, enforce one-time use, or scope the key to specific operations. In FeathersJS, this might manifest as a service that permits key-based access to create or update records without confirming that the key’s associated tenant is allowed to modify that particular record, directly mapping to the OWASP API Top 10 category of Insecure Design and amplifying risks such as data exposure or privilege escalation.
From an API security scanner perspective, these issues are detectable through unauthenticated and authenticated probes that compare access patterns across endpoints and tenants. A scan can identify missing authorization checks in FeathersJS services that accept API keys, surface excessive data exposure in responses, and highlight inconsistent application of security controls across service methods. These findings align with compliance mappings to OWASP API Top 10, SOC2 controls, and GDPR expectations around data minimization and access control, emphasizing the need to validate authorization at the method level rather than relying solely on entry-point authentication.
Api Keys-Specific Remediation in Feathersjs — concrete code fixes
Remediation focuses on ensuring each API key is bound to a tenant or scope and that every service method enforces ownership or access rules. In FeathersJS, this is achieved by extending hooks to validate keys against a permissions store and by adding record-level checks in service methods or hooks.
Example 1: Scoped API key validation in a before hook
const { Forbidden } = require('@feathersjs/errors');
app.service('reports').hooks({
before: {
all: [async context => {
const { apikey } = context.params.query;
const { user } = context.params;
if (!apikey) {
throw new Forbidden('API key is required');
}
// Lookup key metadata from a secure store (pseudo lookup)
const keyRecord = await context.app.service('api-keys').get(apikey, {
query: { $select: ['scope', 'tenantId', 'allowedServices'] }
});
if (!keyRecord || !keyRecord.allowedServices.includes('reports')) {
throw new Forbidden('Invalid or insufficient scope for API key');
}
// Attach tenant-aware info to context for downstream use
context.params.tenantId = keyRecord.tenantId;
}]
}
});
Example 2: Record-level ownership check in a get method
app.service('reports').hooks({
before: {
get: [async context => {
const { id } = context.id;
const { tenantId } = context.params;
const report = await context.app.service('reports').Model.findOne({
where: { id, tenantId }
});
if (!report) {
throw new NotFound('Report not found or access denied');
}
// Ensure the record matches the tenant derived from the validated API key
context.result = report;
}]
}
});
Example 3: Service find with tenant filtering
app.service('reports').hooks({
before: {
find: [async context => {
const { tenantId } = context.params;
const query = context.params.query || {};
// Ensure tenant filter is applied to prevent enumeration across tenants
context.params.query = {
...query,
$and: [{ tenantId }]
};
}]
}
});
These examples demonstrate concrete fixes that align with remediating Insecure Design by enforcing scope-bound API key usage and record-level authorization. By validating keys against tenant and scope metadata and consistently filtering data access by tenantId, a FeathersJS service can reduce the attack surface associated with BOLA/IDOR and insecure design flaws. The approach integrates naturally with existing FeathersJS hook patterns and can be extended with additional checks for rate limiting or sensitive operations, complementing the visibility provided by external scanners that map findings to frameworks such as OWASP API Top 10.