Security

Bloqueio de IP e Security

Catch-all para rotas inválidas, repositories CQRS-ready (Postgres ativo) e API admin em /api/v1/security.

Visão geral

O módulo em src/common/security registra acessos a rotas de API desconhecidas e pode bloquear IPs. Persistência CQRS-ready: Postgres é o store ativo via BlockedIpPostgresRepository; o repository Mongo fica registrado mas não é usado (mesmo padrão do users). A gestão é feita por endpoints REST que exigem JWT e a role super.

Ideias-chave

  • • CatchAllController registra tentativas em rotas inválidas (só production/staging)
  • • BlockedIpPostgresRepository é o store ativo; repo Mongo é CQRS-ready / não usado
  • • Após MAX_ATTEMPTS, o IP é bloqueado (403 nas próximas)
  • • API admin: listar, desbloquear, resetar, remover — JWT + Role.SUPER

Estrutura

Árvoresrc/common/security
src/common/security/
├── security.module.ts
├── security.controller.ts
├── security.service.ts
├── catchall.controller.ts
├── repositories/
│   ├── postgres.repository.ts
│   └── mongo.repository.ts
├── entities/blocked-ip.entity.ts
├── schemas/blocked-ip.schema.ts
└── interfaces/blocked-ip.interface.ts

Catch-all (rotas inválidas)

O CatchAllController só é registrado quando NODE_ENV é production ou staging. Trata rotas não mapeadas com @All('*') e é @Public().

Fluxo

  • • Path de API desconhecido chega ao CatchAllController
  • • registerInvalidRouteAttempt(ip, path, userAgent)
  • • Se attempts ≥ MAX_ATTEMPTS → blocked = true → 403
  • • Caso contrário → 404 JSON { message: "Rota não encontrada" }

Não conta como suspeito

  • • 404 HTML da wiki (Accept: text/html no browser)
  • • Path GraphQL (GRAPHQL_PATH ou /graphql) — rota Apollo Fastify
  • • /favicon.ico (e demais assets de favicon), /robots.txt e /health

SecurityService

As regras de domínio ficam no SecurityService; a persistência passa por BlockedIpPostgresRepository. O repository Mongo existe para CQRS futuro, mas não é chamado.

Constantes

  • • MAX_ATTEMPTS = 1 (bloqueia após este número de hits inválidos)
  • • MAX_PATHS_STORED = 20 (últimos paths guardados no registro)

Métodos principais

  • • registerInvalidRouteAttempt — upsert do IP, incrementa attempts, pode bloquear
  • • getBlockedIps / getSuspiciousIps — listas admin
  • • unblockIp — limpa bloqueio e tentativas
  • • resetAttempts — limpa attempts/block/paths
  • • removeIp — apaga via repository Postgres

API admin

Todas as rotas em /api/v1/security exigem Authorization: Bearer e Role.SUPER.

Endpoints

  • • GET /api/v1/security/blocked-ips — listar IPs bloqueados
  • • GET /api/v1/security/suspicious-ips — IPs com tentativas, ainda não bloqueados
  • • POST /api/v1/security/unblock/:ip — desbloquear (200 / 404)
  • • POST /api/v1/security/reset/:ip — resetar tentativas (200 / 404)
  • • DELETE /api/v1/security/:ip — remover registro (200 / 404)
HTTP RequestGET /api/v1/security/blocked-ips
GET /api/v1/security/blocked-ips
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
HTTP Response200 OK
[
  {
    "id": "uuid-...",
    "ip": "203.0.113.10",
    "attempts": 1,
    "blocked": true,
    "paths": ["/api/v1/unknown"],
    "blockedAt": "2026-07-23T12:00:00.000Z"
  }
]
HTTP RequestPOST /api/v1/security/unblock/:ip
POST /api/v1/security/unblock/203.0.113.10
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
HTTP Response200 OK
{
  "message": "IP 203.0.113.10 desbloqueado com sucesso",
  "data": { "id": "uuid-...", "ip": "203.0.113.10", "blocked": false, "attempts": 0 }
}

Nota Docker / production

Com NODE_ENV=production (como no .env.docker), o catch-all está ativo. Hits em paths de API inexistentes podem bloquear seu IP após MAX_ATTEMPTS. Use a API admin (role super) para desbloquear, ou limpe a tabela blocked_ips.