Rate Limiting Bypass in Feathersjs with Api Keys
Rate Limiting Bypass in Feathersjs with Api Keys — how this specific combination creates or exposes the vulnerability
FeathersJS is a framework for building REST and real-time APIs. When API keys are used for identification but not integrated into the rate-limiting strategy, the effectiveness of rate limiting can be undermined. Rate limiting typically operates at a connection or IP level. If each unique API key is not treated as a distinct subject for limiting, an attacker who obtains a single valid key can make a high number of requests without tripping the mechanism, while different keys used by the same client may bypass aggregate limits intended for a user or application.
Consider a FeathersJS service that applies a global rate limit using a connection-based store such as memory or Redis but does not include the API key value in the rate-limit key. In this scenario, the limit is shared across all requests that originate from the same network path, regardless of which authenticated key is presented. An attacker who compromises a single key can generate many requests, consuming the quota that should be shared among many legitimate users. This is a Rate Limiting Bypass relative to the intended protection scope, because the boundary of enforcement does not align with the boundary of authentication.
Moreover, if the API key is passed as a query parameter rather than in a header, logging or caching layers might inadvertently expose it, and inconsistent key validation could allow malformed or missing keys to be accepted. In such configurations, the rate limiter might not increment correctly for these requests, effectively neutralizing the limit. The combination of per-key authentication and coarse-grained rate limiting creates a scenario where the control intended to protect service availability does not reliably constrain individual authenticated actors.
During a middleBrick scan, such misconfigurations are surfaced with severity ratings and references to relevant attack patterns like OWASP API Top 10 #5: Broken Function Level Authorization, where limits are not enforced per authorization context. The scanner evaluates whether the rate-limiting logic incorporates the authenticated identity (e.g., the API key) into the limiting key. It also checks whether the service properly validates the presence and format of API keys on each request and whether the limit boundaries match the intended security policy. These checks are performed unauthenticated, simulating an attacker who has obtained a valid key but does not possess broader credentials.
Real-world examples include scenarios where Redis-based rate limiting uses only the IP address as the key, or where a middleware counter increments based on session identifiers but excludes the API key. In FeathersJS, if the hook that enforces limits does not read the key from the authentication object and include it in the composite key (e.g., rate_limit:{api_key}), the protection is weaker than intended. An attacker can rotate source IPs or use distributed clients while still reusing a single valid key, effectively bypassing the designed rate limit.
Remediation involves aligning the rate-limiting key with the authenticated identity. The API key should be part of the rate-limiting namespace so that each distinct key has its own limit. This requires changes in the rate-limiting hook or middleware to extract the key from the authentication entry and incorporate it into the limit key. Additionally, ensure that keys are transmitted in secure headers, validated for format on every request, and that the rate limiter uses a consistent backend with appropriate TTL to prevent drift. middleBrick’s per-category breakdown highlights these gaps and provides prioritized findings with remediation guidance to tighten the alignment between authentication and rate control.
Api Keys-Specific Remediation in Feathersjs — concrete code fixes
To remediate Rate Limiting Bypass in FeathersJS when using API keys, update hooks and services so that the rate limiter uses the API key as part of the limiting key. Below is a concrete example using the @feathersjs/authentication and a Redis-based rate limiter. The key idea is to read the authenticated API key from the connection and include it in the rate-limit identifier.
// src/hooks/rate-limit.js
const { RateLimiterRedis } = require('rate-limiter-flexible');
const { GeneralError } = require('@feathersjs/errors');
const redisClient = require('redis').createClient({ url: process.env.REDIS_URL });
const rateLimiter = new RateLimiterRedis({
storeClient: redisClient,
points: 100, // 100 requests
duration: 60 // per 60 seconds
});
module.exports = function () {
return async context => {
const { connection, params } = context;
// In Feathers, after authentication, the API key is available on the connection or params
const apiKey = (connection && connection.apiKey) || (params && params.apiKey);
if (!apiKey) {
throw new GeneralError('API key is required for rate limiting', { code: 401 });
}
// Use the API key as part of the rate-limit key to enforce per-key limits
const key = `rate_limit_apikey:${apiKey}`;
try {
await rateLimiter.consume(key);
} catch (rej) {
throw new GeneralError('Rate limit exceeded', { code: 429 });
}
return context;
};
};
Then, in your service configuration or app-level hooks, ensure this hook runs after authentication so that connection.apiKey or params.apiKey is populated. For example:
// src/app.js
const authentication = require('@feathersjs/authentication');
const jwt = require('@feathersjs/authentication-jwt');
const rateLimitHook = require('./hooks/rate-limit');
app.configure(authentication({
secret: process.env.AUTH_SECRET,
entity: 'user',
multi: true
}));
app.configure(jwt());
// Apply the rate-limit hook globally after auth so apiKey is available
app.hooks({
before: {
all: [rateLimitHook()]
]
});
Ensure that your API key is attached to the connection during authentication. If you use JWTs and map them to API keys, set connection.apiKey in a custom authentication strategy or hook. Also, validate the key format on each request to avoid bypass via malformed input:
// src/hooks/validate-api-key.js
module.exports = function () {
return async context => {
const { apiKey } = context.params;
if (typeof apiKey !== 'string' || !/^[A-Za-z0-9_-]{32,64}$/.test(apiKey)) {
throw new Error('Invalid API key format');
}
// Optionally fetch key metadata from a store to ensure it is active
return context;
};
};
By combining these hooks, the rate limiter’s key includes the API key, ensuring that limits are enforced per key rather than per IP or connection. This addresses the bypass where a single compromised key could exhaust shared limits. middleBrick’s scans can verify that the limiting key includes the authenticated identity and that validation logic is present, surfacing findings with actionable remediation guidance.
Related CWEs: resourceConsumption
| CWE ID | Name | Severity |
|---|---|---|
| CWE-400 | Uncontrolled Resource Consumption | HIGH |
| CWE-770 | Allocation of Resources Without Limits | MEDIUM |
| CWE-799 | Improper Control of Interaction Frequency | MEDIUM |
| CWE-835 | Infinite Loop | HIGH |
| CWE-1050 | Excessive Platform Resource Consumption | MEDIUM |