Skip to content

Política de Retenção de IPs — LGPD + Marco Civil da Internet ​

Última atualização: 2026-03-08 Base legal: Lei 12.965/2014 (Marco Civil, art. 15) + Lei 13.709/2018 (LGPD, art. 16)


LeiArtigoObrigação
Marco Civil da InternetArt. 15Provedor de aplicações deve guardar registros de acesso por 6 meses
LGPDArt. 16Dados pessoais devem ser eliminados após o término do tratamento, salvo obrigação legal

O IP do usuário é dado pessoal (pode identificar indiretamente). A estratégia concilia ambas as leis:

  • 0–6 meses: IP completo armazenado (obrigação legal)
  • Após 6 meses: IP anonimizado via hash truncado (LGPD)

2. Implementação ​

2.1 Onde o IP é armazenado ​

TabelaCampoUso
logs_transacoesipAuditoria de todas as operações do sistema
login_otpsip_origemRate limiting de OTP (TTL: 24h, limpo pelo cron)
portal_login_tentativasipRate limiting do portal (TTL: 7 dias, limpo pelo cron)

2.2 Anonimização Automática (Cron) ​

A tarefa anonimizarIPsAntigos no maintenance-cron executa diariamente:

  1. logs_transacoes: IPs com criado_em < NOW() - 6 meses são substituídos por anon_ + SHA-256 truncado (8 hex chars)

    • Exemplo: 200.123.45.67 → anon_a1b2c3d4
    • O prefixo anon_ evita re-processamento
    • O hash preserva agrupamento (mesmo IP → mesmo hash) sem permitir reversão
    • Campos cidade, estado, pais são mantidos (dados agregados, não pessoais)
  2. login_otps e portal_login_tentativas: Já são limpos pelo cron com TTL muito menor (24h e 7 dias), portanto não precisam de anonimização adicional

2.3 Batch Processing ​

A anonimização processa até 1.000 registros por execução do cron para evitar timeouts. Se houver mais registros pendentes, serão processados na próxima execução diária.


3. Captura de IP Real ​

3.1 Problema Histórico ​

Entre 18/jan/2026 e 08/mar/2026, ~72,6% dos logs capturavam o IP do datacenter Cloudflare (2a06:98c0:3600::103) em vez do IP real do cliente. Causa: o header X-Real-IP era sobrescrito pela infraestrutura Supabase.

3.2 Solução: Header Customizado ​

O Cloudflare Worker (OLP Gateway) injeta o IP real do cliente no header X-OLP-Client-IP, que não é sobrescrito pelo Supabase.

Ordem de prioridade em extractIP():

1. x-olp-client-ip   ← Gateway OLP (header customizado, confiável)
2. x-real-ip          ← Fallback (pode ser sobrescrito em produção)
3. cf-connecting-ip   ← Cloudflare direto (sem Worker)
4. x-forwarded-for    ← Padrão proxies (primeiro IP)

3.3 Ação Manual Necessária ​

⚠️ O código do Cloudflare Worker precisa ser atualizado manualmente no painel Cloudflare para injetar X-OLP-Client-IP em vez de (ou além de) X-Real-IP.

Linha a alterar no Worker:

javascript
// ANTES:
forwardHeaders.set('X-Real-IP', clientIP);

// DEPOIS (adicionar):
forwardHeaders.set('X-OLP-Client-IP', clientIP);
forwardHeaders.set('X-Real-IP', clientIP); // manter para compatibilidade

4. Verificação ​

Como validar que IPs estão sendo capturados corretamente ​

sql
-- IPs reais vs Cloudflare nos últimos 7 dias
SELECT 
  CASE 
    WHEN ip LIKE '2a06:98c0:%' THEN 'cloudflare_dc'
    WHEN ip LIKE 'anon_%' THEN 'anonimizado'
    ELSE 'ip_real'
  END as tipo,
  COUNT(*) 
FROM logs_transacoes 
WHERE criado_em > NOW() - INTERVAL '7 days'
GROUP BY tipo;

Como validar a anonimização ​

sql
-- Registros anonimizados
SELECT COUNT(*) FROM logs_transacoes WHERE ip LIKE 'anon_%';

-- Registros ainda com IP real e > 6 meses (devem ser 0 após cron)
SELECT COUNT(*) FROM logs_transacoes 
WHERE criado_em < NOW() - INTERVAL '6 months' 
AND ip IS NOT NULL 
AND ip NOT LIKE 'anon_%';

5. Referências ​

  • supabase/functions/_shared/logging-helper.ts — extractIP() com prioridade x-olp-client-ip
  • supabase/functions/maintenance-cron/index.ts — Tarefa 11: anonimizarIPsAntigos()
  • docs/architecture/CLOUDFLARE_WORKER_CODE.md — Código do Worker com header customizado
  • docs/architecture/CLOUDFLARE_WORKER_GATEWAY.md — Documentação do Gateway
  • docs/security/AUDIT_LOG.md — Padrões de logging