Skip to content

fix(seguranca): limitar o /events/ingest, unica rota publica de escrita - #30

Merged
janeiaraujo merged 1 commit into
mainfrom
fix/ingest-rate-limit
Aug 3, 2026
Merged

fix(seguranca): limitar o /events/ingest, unica rota publica de escrita#30
janeiaraujo merged 1 commit into
mainfrom
fix/ingest-rate-limit

Conversation

@janeiaraujo

Copy link
Copy Markdown
Owner

O problema

O PR #23 protegeu as rotas de /api/auth, mas o /api/events/ingest ficou de fora — e ele é a única rota pública de escrita do sistema, exposta à internet para Zabbix/Grafana/Datadog/Sentry chamarem.

Só havia o limite global de 100/min. Um token vazado ou uma ferramenta em loop podia inundar a base de eventos — e, de quebra, consumir a cota mensal de eventos do plano do tenant.

A decisão: limite por token, não por IP

Este é o ponto que merece atenção na revisão. Ferramentas de monitoramento de uma mesma empresa costumam sair pelo mesmo IP de saída (NAT). Se o limite fosse por IP, uma ferramenta ruidosa derrubaria a ingestão de todas as outras da mesma rede — falha em cascata a partir de um problema isolado.

Com limite por token, cada fonte tem sua própria cota. Sem token identificável, cai para o IP.

120/min é folgado para monitoramento normal e ainda contém um loop descontrolado.

Validação

Subi o Fastify isolado reproduzindo o cenário exato:

fonte ruidosa (8 req, limite 5): 5 x 200, 3 x 429
outra fonte, mesmo IP:           200

Ou seja: a fonte que estoura é barrada, e a outra — no mesmo IP — continua funcionando. Era exatamente o comportamento que a escolha por token pretendia garantir.

Test plan

  • Isolamento por token verificado empiricamente
  • npm test → 25 passam
  • Com o app rodando: enviar um burst acima de 120/min com um token e confirmar 429
  • Confirmar que outro token continua ingerindo normalmente

O PR #23 protegeu as rotas de auth, mas o /ingest ficou de fora - e ele
e a unica rota publica de escrita do sistema, exposta a internet para
Zabbix/Grafana/Datadog chamarem. So havia o limite global de 100/min,
entao um token vazado ou uma ferramenta em loop podia inundar a base de
eventos (e consumir a cota de eventos do plano do tenant).

Limite por TOKEN, nao por IP: varias fontes costumam sair do mesmo IP
de saida (NAT), e limitar por IP faria uma ferramenta ruidosa derrubar
a ingestao das demais. Sem token identificavel, cai para o IP.

120/min e folgado para monitoramento normal e ainda contem um loop.

Verificado subindo o Fastify isolado: a fonte que estoura o limite leva
429 enquanto outra fonte, no mesmo IP, segue recebendo 200.
Copilot AI review requested due to automatic review settings August 3, 2026 19:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Este PR adiciona um rate limit específico para o endpoint público de ingestão de eventos (POST /api/events/ingest), visando conter abuso/loops e reduzir o risco de consumo indevido da cota mensal de eventos por tenant.

Changes:

  • Adiciona rate limit por rota em /api/events/ingest, com chave preferencial por token (fallback para IP).
  • Documenta no CONTRIBUTING.md que esta é a única rota pública de escrita e que o limite é por token.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
CONTRIBUTING.md Documenta a decisão de rate limit por token para a rota pública de ingestão.
backend/src/modules/events/events.routes.js Configura rate limit dedicado para /ingest (max 120/min) com keyGenerator baseado em token/IP.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +55 to +59
rateLimit: {
max: 120,
timeWindow: '1 minute',
keyGenerator: (request) => request.headers['x-api-token'] || request.ip
}
@janeiaraujo
janeiaraujo merged commit 48d82cf into main Aug 3, 2026
4 checks passed
@janeiaraujo
janeiaraujo deleted the fix/ingest-rate-limit branch August 3, 2026 23:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants