Why Fixed-Window Rate Limiters Fail (And How to Fix Them with Math)

Iniciado por joomlamz, Hoje at 14:25

Respostas: 1   |   Visualizações: 5

Tópico anterior - Tópico seguinte

0 Membros e 1 Visitante estão a ver este tópico.

Saudações, comunidade do **webmastersmz.com**!

Como especialista em tecnologia, analisei recentemente um tópico em inglês muito pertinente para quem gere tráfego e infraestrutura web: **"Why Fixed-Window Rate Limiters Fail (And How to Fix Them with Math)"** (Porque é que os Limitadores de Taxa de Janela Fixa Falham e Como os Corrigir com Matemática).

Deixem-me partilhar connosco os pontos principais desta discussão técnica e o porquê de termos de repensar a forma como protegemos as nossas APIs e servidores.

### O Problema da "Janela Fixa" (Fixed-Window)
O algoritmo de janela fixa é o método mais básico de *Rate Limiting* (limitação de taxa). Ele divide o tempo em blocos fixos (por exemplo, 1 minuto) e permite um número máximo de pedidos por bloco. No entanto, ele tem uma falha arquitetónica grave conhecida como **o problema do pico na fronteira (boundary spike)**:

1. **O Cenário de Falha:** Imaginem um limite de 100 pedidos por minuto. Um cliente malicioso (ou um pico legítimo de tráfego) pode consumir todos os 100 pedidos no último segundo do minuto $A$ (ex: 12:00:59) e, de seguida, enviar mais 100 pedidos no primeiro segundo do minuto $B$ (ex: 12:01:00).
2. **O Impacto:** Na prática, o servidor processou 200 pedidos num intervalo de apenas 2 segundos, o que pode deitar abaixo a base de dados ou sobrecarregar a aplicação, violando completamente o objetivo do limitador.

### A Solução Matemática
O artigo aponta que a matemática é a melhor aliada para resolver este problema, destacando alternativas muito mais robustas:

* **Janelas Deslizantes (Sliding Windows / Sliding Log):** Em vez de reiniciar o contador abruptamente a cada minuto, o algoritmo calcula o tráfego com base numa média ponderada do tempo decorrido ou regista os timestamps exatos de cada pedido. Isto elimina os picos nas fronteiras das janelas.
* **Algoritmo do Balde com Fugas (Leaky Bucket) e Balde de Tokens (Token Bucket):** Estes métodos utilizam equações diferenciais simples para garantir que os pedidos são processados a um ritmo constante ou que os tokens são reabastecidos de forma gradual e contínua, garantindo suavidade no fluxo de tráfego.

### Para Debate no Fórum
Encaro este tópico como essencial para qualquer administrador de sistemas ou programador web. Deixo aqui algumas questões para debatermos na nossa comunidade:

1. *Quais têm sido as vossas abordagens para mitigar ataques de força bruta ou DDoS a nível de aplicação?*
2. *Já implementaram Sliding Windows nas vossas APIs ou continuam a confiar em implementações nativas mais simples (como as da Nginx via `limit_req`)?*
3. *Como é que lidam com o impacto na performance ao calcular limites baseados em registos temporais (timestamps) em larga escala?*

Deixem as vossas opiniões e experiências nos comentários abaixo. Vamos enriquecer este debate técnico!

---

Para garantir que os vossos projetos, APIs e fóruns rodam sem falhas e com a máxima estabilidade, convido-vos a conhecer as soluções de alojamento de alta performance da AplicHost em [https://aplichost.com](https://aplichost.com).

Why Fixed-Window Rate Limiters Fail (And How to Fix Them with Math)



Tópico: Why Fixed-Window Rate Limiters Fail (And How to Fix Them with Math)
Categoria: Tutoriais | Programação & Tecnologia
Idioma Principal: Português (Conteúdo de Tecnologia)

Descrição do Conteúdo / Informações:
-------------------------------------------------------------------------
If you've ever built an Express API, you've probably reached for standard rate-limiting middleware to protect your login or payment endpoints from DDoS and brute-force attacks.

Under the hood, most simple limiters use a Fixed-Window Counter. It's easy to write: count incoming requests, and once the minute rolls over, reset the counter to zero.

However, from a security and algorithmic standpoint, Fixed-Window counters have a massive blind spot.



The Boundary Vulnerability (The 2-Second Spike)


Imagine your endpoint allows a maximum of 100 requests per minute, resetting every full minute on the clock (:00).

Here is how an attacker bypasses that limit without breaking your rules:

• At 12:00:59, the attacker fires 100 requests. (Allowed: 100/100 used).

• At 12:01:00, the clock resets your counter back to 0.

• At 12:01:01, the attacker fires another 100 requests. (Allowed: 100/100 used).

To your server code, everything looks fine. But in reality, 200 requests slammed your backend within a 2-second window. In FinTech or authentication systems, that burst is more than enough to overwhelm payment gateways or run a successful credential-stuffing attack.



The Algorithmic Fix: Sliding Window Counter


To stop boundary spikes, we need a continuously sliding window rather than a rigid clock reset.



Attempt 1: The Sliding Window Log (High Memory)


You store a timestamps array (a Deque) for every user request and drop timestamps older than 60 seconds. While accurate, storing every single request timestamp takes $O(N)$ space. If your API receives millions of requests, your server memory dies instantly.



Attempt 2: Sliding Window Counter (Optimal O(1) Math)


Instead of keeping thousands of timestamps, we track only two integers: the request count of the previous window and the count of the current window.

When a request arrives, we calculate an estimated request count by weighting the previous window based on how much time has passed in the current window:

Estimated Requests=Current Count+(Previous Count×Window SizeWindow Size−Time Elapsed​)

If the time elapsed in the current window is 75%, we only count 25% of the previous window's traffic.


Time Complexity: O(1) lookup and arithmetic.


Space Complexity: O(1) memory footprint (just two counter variables per IP).



Building the Middleware in Node.js


Here is a lightweight implementation using JavaScript Map to track state:

class SlidingWindowRateLimiter {
constructor(limit, windowMs) {
this.limit = limit; // e.g., 100 requests
this.windowMs = windowMs; // e.g., 60000ms (1 minute)
this.hits = new Map();
}

isAllowed(ip) {
const now = Date.now();
const currentWindowKey = Math.floor(now / this.windowMs);
const timeElapsedInCurrentWindow = now % this.windowMs;

const record = this.hits.get(ip) || {
prevWindowKey: currentWindowKey - 1,
prevCount: 0,
currWindowKey: currentWindowKey,
currCount: 0,
};

// Roll windows over if time has progressed
if (record.currWindowKey !== currentWindowKey) {
if (record.currWindowKey === currentWindowKey - 1) {
record.prevCount = record.currCount;
} else {
record.prevCount = 0; // Previous window is too old
}
record.currWindowKey = currentWindowKey;
record.currCount = 0;
}

// Calculate sliding weight formula
const weight = (this.windowMs - timeElapsedInCurrentWindow) / this.windowMs;
const estimatedRequests = Math.floor(record.prevCount * weight) + record.currCount;

if (estimatedRequests >= this.limit) {
return { allowed: false, currentCount: estimatedRequests };
}

// Increment and store current count
record.currCount += 1;
this.hits.set(ip, record);

return { allowed: true, currentCount: estimatedRequests + 1 };
}
}

// Express Middleware Wrap
const limiter = new SlidingWindowRateLimiter(100, 60000);

function rateLimiterMiddleware(req, res, next) {
const clientIP = req.ip || req.headers['x-forwarded-for'];
const result = limiter.isAllowed(clientIP);

res.setHeader('X-RateLimit-Limit', 100);
res.setHeader('X-RateLimit-Remaining', Math.max(0, 100 - result.currentCount));

if (!result.allowed) {
return res.status(429).json({
error: 'Too Many Requests',
message: 'Rate limit exceeded. Please try again shortly.',
});
}

next();
}



Why This Matters for High-Performance Systems



Sub-Millisecond Speed: The decision math executes in fractions of a microsecond without iterating over huge arrays.


Boundary Smoothness: An attacker trying the 12:00:59 / 12:01:01 spike will be blocked instantly because the weight of the 12:00:59 burst carries over into the calculation.


Production Readiness: In a distributed multi-node infrastructure, this exact math scales cleanly to Redis using simple INCR and hash keys.

Applying basic competitive programming data structures and math to API security turns naive middleware into enterprise-grade defense.


Joomlamz
Consultoria em Informática
-------------------------------------------------------
Especialista em Sistemas Web & Manutenção de Servidores.
A desenvolver o novo AplPortal com suporte a PHP 8.
Precisa de ajuda profissional? Contacte-me.

Tags: