Skip to content

Design: Detecção de Anomalias de Acesso

Status: 📋 Design (não implementado) Pré-requisitos: IP real funcionando (✅), Painel de Sessões Ativas (pendente) Última atualização: 2026-03-08


1. Objetivo

Identificar acessos anômalos (ex: login de estado/país diferente do habitual) e proteger contas comprometidas com notificações progressivas e bloqueio temporário.


2. Fluxo Proposto

Login (verify-otp)

  ├─ Registra sessão em sessoes_ativas (IP real, geo, user_agent, device_fingerprint)

  ├─ Consulta: IPs/localizações distintas nos últimos 30 dias
  │     SELECT DISTINCT ip, cidade, estado, pais
  │     FROM sessoes_ativas
  │     WHERE usuario_id = X AND criado_em > NOW() - 30 days

  ├─ Baseline do usuário:
  │     Padrão normal = 1-3 IPs (casa, escola, celular)
  │     Mesma cidade/estado = esperado

  ├─ Se novo IP de estado/país diferente do baseline:
  │     Registra anomalia em anomalias_acesso
  │     Incrementa contador

  │   3-5 anomalias em 30 dias → NOTIFICAÇÃO
  │     • Push via ntfy.sh para admin
  │     • Notificação in-app para o usuário
  │     • Texto: "Detectamos acesso de {cidade}/{estado}. Se não foi você, altere sua senha."

  │   5+ anomalias em 30 dias → SOFT-BLOCK
  │     • Marca usuario.requer_verificacao = true
  │     • Próximo login exige verificação de identidade
  │     • Opções para o usuário:
  │       1. Re-validação OTP no telefone cadastrado
  │       2. Confirmar dados pessoais (nome completo + DN)
  │       3. Encerrar todas as sessões ativas
  │     • Admin recebe alerta push com dados do incidente

  └─ Registro em logs_transacoes:
       acao: "seguranca.anomalia_detectada" / "seguranca.soft_block_ativado"

3. Tabela Proposta: anomalias_acesso

sql
CREATE TABLE anomalias_acesso (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  usuario_id UUID REFERENCES usuarios(id) ON DELETE CASCADE,
  ip TEXT NOT NULL,
  cidade TEXT,
  estado TEXT,
  pais TEXT,
  user_agent TEXT,
  tipo TEXT NOT NULL, -- 'novo_estado', 'novo_pais', 'ip_suspecto'
  resolvido BOOLEAN DEFAULT false,
  resolvido_em TIMESTAMPTZ,
  criado_em TIMESTAMPTZ DEFAULT now()
);

4. Métricas de Decisão

MétricaThresholdAção
IPs distintos (30 dias)> 5Monitorar
Estados distintos (30 dias)> 2Anomalia
Países distintos (30 dias)> 1Anomalia grave
Anomalias acumuladas (30 dias)3-5Notificação
Anomalias acumuladas (30 dias)5+Soft-block

5. Dependências

ComponenteStatusNecessário para
IP real via X-OLP-Client-IP✅ ImplementadoDados confiáveis de geolocalização
sessoes_ativas (tabela)❌ PendenteBaseline de IPs por usuário
Painel de Sessões Ativas (UI)❌ PendenteGestão de sessões pelo usuário
Verificação de identidade❌ PendenteDesbloqueio pós soft-block
anomalias_acesso (tabela)❌ PendenteRegistro de anomalias

6. Notas de Implementação

  • A análise de anomalias deve rodar no login (verify-otp), não em cron, para resposta imediata
  • O soft-block NÃO impede login — apenas exige uma etapa extra de verificação
  • IPs de VPNs corporativas e redes escolares (NAT) devem ser tratados com cuidado — agrupar por cidade/estado em vez de IP individual
  • A janela de 30 dias é suficiente para capturar sazonalidade (ex: viagem) sem gerar falsos positivos

7. Referências

  • docs/security/IP_RETENTION_LGPD.md — Política de retenção e captura de IP real
  • docs/plans/SESSION_MANAGEMENT_ALERTS.md — Plano do Painel de Sessões Ativas
  • supabase/functions/_shared/logging-helper.tsextractIP() com x-olp-client-ip