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.67anon_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.tsextractIP() 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