Rate Limiting Bypass in Express with Api Keys
Rate Limiting Bypass in Express with Api Keys — how this specific combination creates or exposes the vulnerability
In Express applications, using API keys alone does not enforce rate limits and can create a bypass scenario when rate limiting is not applied uniformly or is tied to an identity that can be abused. A common pattern is to read the API key from headers, query parameters, or a bearer token, then allow the request to proceed without a per-key or per-client limit. If middleware that enforces rate limits is configured to skip counting when an API key is missing or invalid, an attacker who discovers or guesses a valid key can exhaust the quota for that key while other identifiers (such as IP) may still be rate-limited separately.
Consider an Express route that checks for an API key but does not incorporate the key into the rate-limiting bucket:
const rateLimit = require('express-rate-limit');
const ApiKeyValidator = require('./apiKeyValidator'); // custom module
const limiter = rateLimit({
windowMs: 60 * 1000,
max: 100,
keyGenerator: (req) => req.ip, // rate limit by IP only
skip: (req) => !ApiKeyValidator.hasKey(req) // skip if no key
});
app.use('/api/v1/data', limiter, (req, res) => {
const key = req.headers['x-api-key'];
if (!ApiKeyValidator.isValidKey(key)) {
return res.status(401).json({ error: 'Invalid API key' });
}
res.json({ data: 'sensitive data' });
});
In this setup, if an attacker obtains a valid API key, they can send up to 100 requests per minute under that key while the limiter still counts requests by IP. Because the limiter skips when the key is missing, the attacker can rotate among multiple stolen keys or reuse a single key to effectively bypass intended per-client throttling. The key is treated as an authorization check rather than a rate-limiting identifier, creating a mismatch between authentication and rate control.
Another bypass pattern involves middleware ordering. If API key validation is performed after the rate limiter, the limiter may apply to unauthenticated requests (without a key), while authenticated requests with a valid key may be subject to a higher limit or no limit at all if the limiter is scoped incorrectly. For example:
app.use('/api/v1/admin', apiKeyCheck, adminLimiter); // adminLimiter may be lenient or missing
app.use('/api/v1/public', publicLimiter);
If adminLimiter is not configured with a strict max and windowMs, or if it uses a broader key generator that does not incorporate the API key, an attacker who authenticates as an admin client can drive higher throughput. The presence of an API key should tighten controls, not relax them, and the rate limiter must include the key in its identifier to ensure per-key quotas are enforced consistently.
SSRF and external probing can also reveal whether rate limiting is key-aware. By sending requests with and without keys, an attacker can infer whether the server applies different limits based on authentication state. This can be part of automated reconnaissance during an unauthenticated scan, where endpoints that expose different behavior depending on key presence indicate a potential bypass.
Api Keys-Specific Remediation in Express — concrete code fixes
To prevent rate limiting bypass when using API keys, tie the rate limiter to the API key itself and ensure validation and throttling happen within the same logical flow. The key should be treated as part of the rate-limiting identity, and the middleware should not skip counting for authenticated requests.
Use a key-aware limiter configuration that includes the API key in the keyGenerator. If the key is present but invalid, reject the request before the limiter to avoid leaking information about valid keys through timing differences. Here is a secure pattern:
const rateLimit = require('express-rate-limit');
const apiKeyLimiter = rateLimit({
windowMs: 60 * 1000,
max: 100,
keyGenerator: (req) => {
// Prefer header, fall back to query for compatibility
return req.headers['x-api-key'] || req.query.api_key;
},
skip: (req) => {
// Do not skip if key is missing; handle missing key later to keep behavior consistent
return false;
}
});
app.use('/api/v1/data', apiKeyLimiter, (req, res, next) => {
const key = req.headers['x-api-key'] || req.query.api_key;
if (!ApiKeyValidator.isValidKey(key)) {
return res.status(401).json({ error: 'Invalid or missing API key' });
}
next();
});
app.use('/api/v1/data', (req, res) => {
res.json({ data: 'rate-limited and authenticated data' });
});
In this configuration, the rate limiter counts requests per API key, ensuring that a compromised key cannot be used to exceed per-key quotas. By keeping the validation after the limiter’s identifier extraction but before business logic, you maintain consistent behavior and avoid signaling the presence of valid keys through different response paths.
For multi-tenant or shared-key scenarios, use namespaced keys or key prefixes to isolate clients and enforce organization-level limits. Rotate keys regularly and monitor usage via logs to detect spikes that may indicate abuse or accidental exhaustion.
If you use a dedicated security or API management layer, ensure its rate-limiting rules mirror the same key scope as your Express middleware. Misalignment between gateway limits and application-level limits can still allow bypasses. The CLI tool (middlebrick scan <url>) can detect inconsistencies between authentication and rate limiting in unauthenticated scans, and the GitHub Action can enforce a minimum security score in CI/CD pipelines to catch regressions before deployment.
When adopting continuous monitoring (Pro plan), configure alerts for abnormal request volumes tied to specific API keys. Dashboard tracks score changes over time and can surface findings related to authentication and rate limiting, helping you maintain a robust posture without relying on manual audits.
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 |