🛡️ Fix global DoS vulnerability in rate limiter (FIFO eviction)
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
This commit is contained in:
@@ -59,9 +59,9 @@ describe('checkRateLimit', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('does not crash when store is at capacity and still accepts new IPs', () => {
|
it('does not crash when store is at capacity and still accepts new IPs', () => {
|
||||||
// Fill the store beyond MAX_STORE_ENTRIES (1 000 in test env or 10 000 in prod)
|
// Fill the store beyond MAX_STORE_ENTRIES (10_000 in prod)
|
||||||
// by hammering with unique IPs; evictIfFull must silently evict stale entries.
|
// by hammering with unique IPs; evictIfFull must silently evict stale entries.
|
||||||
const uniqueIps = Array.from({ length: 200 }, (_, i) => `evict-test-ip-${i}-${Date.now()}`);
|
const uniqueIps = Array.from({ length: 10005 }, (_, i) => `evict-test-ip-${i}-${Date.now()}`);
|
||||||
for (const ip of uniqueIps) {
|
for (const ip of uniqueIps) {
|
||||||
checkRateLimit(ip, 5);
|
checkRateLimit(ip, 5);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,8 +63,12 @@ export function checkRateLimit(
|
|||||||
// New window or expired
|
// New window or expired
|
||||||
if (!entry || now > entry.expiresAt) {
|
if (!entry || now > entry.expiresAt) {
|
||||||
if (store.size >= MAX_STORE_ENTRIES) {
|
if (store.size >= MAX_STORE_ENTRIES) {
|
||||||
// Store is completely full with active IPs -> block to prevent memory exhaustion & rate limit bypass
|
// Store is full. Evict the oldest entry (FIFO) to prevent a global DoS
|
||||||
return { success: false, remaining: 0, resetAt: now + windowMs };
|
// where an attacker fills the store with spoofed IPs, locking out legitimate users.
|
||||||
|
const oldestKey = store.keys().next().value;
|
||||||
|
if (oldestKey !== undefined) {
|
||||||
|
store.delete(oldestKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const resetAt = now + windowMs;
|
const resetAt = now + windowMs;
|
||||||
store.set(key, { count: 1, expiresAt: resetAt });
|
store.set(key, { count: 1, expiresAt: resetAt });
|
||||||
|
|||||||
Reference in New Issue
Block a user