Distributed Denial Of Service in Feathersjs with Api Keys
Distributed Denial Of Service in Feathersjs with Api Keys — how this specific combination creates or exposes the vulnerability
A DDoS attack against a FeathersJS service that uses API keys can exploit the interaction between authentication and resource consumption. API keys are typically validated early in the request lifecycle, but if key validation or subsequent service logic is computationally expensive or unthrottled, an attacker can amplify resource usage. For example, an attacker may send many requests with valid but low-privilege keys, each triggering heavy database lookups, file I/O, or external HTTP calls. FeathersJS hooks and services run for every request; if these hooks perform unbounded queries or iterate over large datasets without pagination, the server’s CPU and memory usage can spike. Even when keys are verified, the application may still process middleware, deserialization, and business logic before rejecting the request, which means resources are consumed per request. Rate limiting that is applied after authentication can allow a burst to slip through, especially when the same API key is reused across many clients. In a distributed scenario, multiple compromised clients or bots can coordinate using different valid keys, making traffic appear legitimate and bypass simple per-IP protections. This combination increases the impact of a volumetric or slowloris-style attack because the server must perform work for each authenticated request, potentially exhausting thread pools, database connections, or event loop capacity, leading to service degradation for legitimate users.
Api Keys-Specific Remediation in Feathersjs — concrete code fixes
Apply targeted mitigations in FeathersJS to reduce DDoS risk when API keys are used. Start with rate limiting at the key level using a reliable store such as Redis. This ensures that even with valid keys, excessive requests are throttled before consuming significant resources.
const rateLimit = require('feathers-rate-limit');
const RedisStore = require('rate-limiter-flexible').RedisStore;
const redisClient = require('redis').createClient({ url: process.env.REDIS_URL });
app.configure(rateLimit({
store: new RedisStore({
sendCommand: (...args) => redisClient.sendCommand(args),
}),
points: 100, // 100 requests
duration: 60, // per 60 seconds
keyPrefix: 'rl',
keyExtractor: (context) => context.params.query['api_key'] || context.params.provider, // use API key if present
}));
Ensure key validation logic is lightweight. Avoid heavy synchronous operations or remote calls during authentication. If you use custom authentication, prefer asynchronous checks with short timeouts and early exits for invalid keys.
// Custom authentication hook: keep it fast const { AuthenticationService } = require('@feathersjs/authentication'); const { iff, isProvider } = require('feathers-hooks-common'); class ApiKeyService extends AuthenticationService { async create(authParams, { provider }) { const { api_key } = authParams; if (!api_key) throw new Error('Unauthorized'); // Perform a fast lookup, e.g., via indexed field in DB const keyRecord = await this.app.service('api-keys').get(api_key, { payload: false }); if (!keyRecord || keyRecord.active !== true) { throw new Error('Invalid API key'); } // Attach minimal metadata to avoid downstream heavy joins return { ...authParams, keyId: keyRecord.id, scope: keyRecord.scope || 'default' }; } } // Use hooks to enforce scope and rate limit per key const protectHook = iff( isProvider('external'), (context) => { if (!context.params.accountId) { throw new Error('Forbidden: missing account context'); } // Enforce per-key quotas via context or rate-limiter client return context; } ); app.use('/secure', protectHook);Apply request validation and payload limits early to prevent resource exhaustion from large or malformed payloads. Use schema validation and restrict content length to reduce CPU and memory strain.
const { iff, isProvider } = require('feathers-hooks-common'); const { validator } = require('feathers-joi'); app.use('/submit', validator({ body: { data: Joi.object({ items: Joi.array().max(50).items(Joi.string().max(256)), }).required(), }, })); // Hook to enforce per-key request-size limits before processing app.service('submit').hooks({ before: [ iff(isProvider('external'), async (context) => { const raw = JSON.stringify(context.data); if (raw.length > 1024 * 1024) { // 1 MB limit throw new Error('Payload too large'); } return context; }), ], });Monitor and enforce connection and timeout settings on external HTTP calls and database queries initiated by service logic. Use circuit breaker patterns in your application layer to prevent cascading resource exhaustion when downstream dependencies become slow or unresponsive.
// Example using axios with timeout and pool limits for external calls const axios = require('axios'); const client = axios.create({ timeout: 2000, maxContentLength: 1024 * 100, maxBodyLength: 1024 * 100, }); app.hooks({ before: { async externalCall(context) { try { const response = await client.get(context.params.url); context.result = response.data; } catch (error) { throw new Error('External service unavailable'); } return context; }, }, });middleBrick can support these efforts by scanning your FeathersJS endpoints and identifying missing rate limits, heavy unthrottled hooks, and exposed high-risk endpoints. Its checks include Authentication, BOLA/IDOR, Rate Limiting, and Unsafe Consumption, which map to controls relevant to DDoS mitigation. The dashboard and CLI integrate into CI/CD so you can fail builds if risk scores drop below your chosen threshold, helping maintain a more resilient API surface.