FNSP Web Page

Limitar pedidos sem incomodar quem visita

Um formulário aberto na internet acaba por receber a visita de um robô. Sem limite nenhum, uma tarde chega para encher a base de dados — e o custo de a limpar é sempre maior do que o de a proteger.

1. Janela fixa: simples, com um defeito

const contagens = new Map();

function dentroDoLimite(chave, maximo, janelaMs) {
  const agora = Date.now();
  const registo = contagens.get(chave);

  if (!registo || agora > registo.expiraEm) {
    contagens.set(chave, { total: 1, expiraEm: agora + janelaMs });
    return true;
  }

  if (registo.total >= maximo) return false;
  registo.total++;
  return true;
}

O defeito está na fronteira: com um limite de 5 por minuto, alguém pode fazer 5 no último segundo de um minuto e 5 no primeiro do seguinte — 10 em dois segundos. Para um formulário de comentários é irrelevante. Para uma API de pagamentos, não.

2. Janela deslizante

const pedidos = new Map(); // chave → lista de instantes

function dentroDoLimite(chave, maximo, janelaMs) {
  const agora = Date.now();
  const recentes = (pedidos.get(chave) ?? []).filter((t) => agora - t < janelaMs);

  if (recentes.length >= maximo) return false;

  recentes.push(agora);
  pedidos.set(chave, recentes);
  return true;
}

Guarda os instantes e conta os que ainda contam. Gasta mais memória, e não tem fronteiras a explorar.

3. Responder como deve ser

res.status(429).set('Retry-After', '60').json({
  erro: 'Demasiados pedidos. Tente daqui a um minuto.',
});

O código é o 429, e o Retry-After diz ao cliente quando pode voltar. Um cliente bem feito espera; sem esse cabeçalho, tenta outra vez já.

4. Os dois avisos que interessam

  • Em memória só funciona com um processo. Com duas instâncias atrás de um balanceador, cada uma conta a sua metade e o limite real é o dobro. A partir daí, é preciso um sítio partilhado — Redis, tipicamente.

  • A chave tem de ser a certa. Por IP apanha o robô e apanha também um escritório inteiro atrás do mesmo IP. Onde há sessão, limite-se por utilizador; onde não há, por IP, e com folga suficiente para uma pessoa normal nunca lá chegar.

Comentários

Ainda ninguém comentou este artigo.

Voltar ao blog

Gostávamos de saber quantas pessoas visitam o site, com o Google Analytics. Sem a sua autorização não corre nada. Política de Privacidade.