🔒 Auditoria de Segurança: Webhooks e Criptografia de Dados Sensíveis

Data da Auditoria: 2026-04-18
Escopo: Autenticação de Webhooks (1.3.3) e Criptografia de Dados Sensíveis (1.3.4)
Baseado em: Supabase Postgres Best Practices - Security Guidelines


📋 Sumário Executivo

CategoriaCríticasAltasMédiasBaixasInfo
Webhooks02312
Criptografia01223
TOTAL03535

1.3.3 Autenticação de Webhooks

1.1 Segredos de Webhook

✅ FINDING 1.1.1: Segredos não expostos em código fonte

  • Severidade: INFO
  • Localização: /data/user/0/com.openshell/files/rv-adv/supabase/functions/ti-webhook-receiver/index.ts:216
  • Descrição: Os segredos são obtidos via Deno.env.get() corretamente, não hardcoded
  • Evidência:
const secret = Deno.env.get("TI_WEBHOOK_SECRET") || "";
  • Recomendação: Manter prática atual. Garantir que .env nunca seja commitado.
  • CWE: CWE-798 (Use of Hard-coded Credentials) - NÃO APLICÁVEL (prática correta)

⚠️ FINDING 1.1.2: Falta de validação de complexidade do segredo

  • Severidade: MÉDIA
  • Localização: /data/user/0/com.openshell/files/rv-adv/supabase/functions/ti-webhook-receiver/index.ts:216 e /data/user/0/com.openshell/files/rv-adv/supabase/functions/inss-webhook/index.ts:62
  • Descrição: Não há validação de complexidade mínima para os segredos de webhook (tamanho, caracteres especiais, etc.)
  • Evidência:
const secret = Deno.env.get("TI_WEBHOOK_SECRET") || "";
// Nenhuma validação de complexidade
  • Recomendação: Implementar validação de complexidade:
function validateSecret(secret: string): boolean {
  // Mínimo 32 caracteres, com letras, números e símbolos
  return secret.length >= 32 && /[A-Za-z]/.test(secret) && /[0-9]/.test(secret) && /[^A-Za-z0-9]/.test(secret);
}
  • CWE: CWE-521 (Weak Password Requirements)

⚠️ FINDING 1.1.3: Sem política de rotação de segredos documentada

  • Severidade: BAIXA
  • Localização: N/A (processo)
  • Descrição: Não há evidência de política de rotação de segredos de webhook
  • Evidência: Ausência de documentação ou automação de rotação
  • Recomendação:
    • Implementar rotação a cada 90 dias
    • Usar Supabase Vault para gerenciamento de segredos
    • Documentar processo de rotação em docs/SECURITY.md
  • CWE: CWE-324 (Use of a Key Past its Expiration Date)

1.2 Validação de Assinaturas

✅ FINDING 1.2.1: HMAC-SHA256 implementado em ti-webhook-receiver

  • Severidade: INFO
  • Localização: /data/user/0/com.openshell/files/rv-adv/supabase/functions/ti-webhook-receiver/index.ts:98-122
  • Descrição: Implementação correta de HMAC-SHA256 via Web Crypto API
  • Evidência:
async function verifyHMAC(payload: string, secret: string, hexSignature: string): Promise<boolean> {
  const key = await crypto.subtle.importKey("raw", keyData, { name: "HMAC", hash: "SHA-256" }, false, ["verify"]);
  return crypto.subtle.verify("HMAC", key, sigBytes, encoder.encode(payload));
}
  • Recomendação: Manter implementação atual
  • CWE: CWE-328 (Reversible One-Way Hash) - MITIGADO

✅ FINDING 1.2.2: HMAC-SHA256 implementado em inss-webhook

  • Severidade: INFO
  • Localização: /data/user/0/com.openshell/files/rv-adv/supabase/functions/inss-webhook/index.ts:7-23
  • Descrição: Implementação correta de HMAC-SHA256 via Web Crypto API
  • Evidência:
async function verifyHMAC(payload: string, secret: string, hexSignature: string): Promise<boolean> {
  const key = await crypto.subtle.importKey("raw", keyData, { name: "HMAC", hash: "SHA-256" }, false, ["verify"]);
  return crypto.subtle.verify("HMAC", key, sigBytes, encoder.encode(payload));
}
  • Recomendação: Manter implementação atual
  • CWE: CWE-328 - MITIGADO

✅ FINDING 1.2.3: Comparação timing-safe implementada

  • Severidade: INFO
  • Localização: /data/user/0/com.openshell/files/rv-adv/supabase/functions/ti-webhook-receiver/index.ts:79-93
  • Descrição: Função timingSafeEqual previne timing attacks
  • Evidência:
function timingSafeEqual(a: string, b: string): boolean {
  if (a.length !== b.length) {
    let diff = 0;
    for (let i = 0; i < Math.max(a.length, b.length); i++) {
      diff |= (a.charCodeAt(i) || 0) ^ (b.charCodeAt(i) || 0);
    }
    return false;
  }
  let diff = 0;
  for (let i = 0; i < a.length; i++) {
    diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
  }
  return diff === 0;
}
  • Recomendação: Manter implementação atual
  • CWE: CWE-208 (Observable Timing Discrepancy) - MITIGADO

1.3 Fallback de Autenticação

⚠️ FINDING 1.3.1: Múltiplos métodos de fallback aumentam superfície de ataque

  • Severidade: ALTA
  • Localização: /data/user/0/com.openshell/files/rv-adv/supabase/functions/ti-webhook-receiver/index.ts:134-182
  • Descrição: Três métodos de autenticação (HMAC, Bearer, Query token) criam múltiplos vetores de ataque. Query token é particularmente vulnerável a vazamento em logs.
  • Evidência:
// Estratégia 1: HMAC-SHA256
const signatureHeader = req.headers.get("X-Webhook-Signature");

// Estratégia 2: Bearer token
const authHeader = req.headers.get("Authorization");

// Estratégia 3: Query param ?token=...
const queryToken = url.searchParams.get("token");
  • Risco:
    • Query tokens são logados em proxies, servidores web e histórico de navegador
    • URLs com tokens podem vazar via Referer header
    • Logs de aplicação podem capturar query params
  • Recomendação:
    • IMEDIATA: Remover fallback para query token (estratégia 3)
    • Manter apenas HMAC como método primário
    • Bearer token pode ser mantido para compatibilidade legada com aviso de depreciação
  • CWE: CWE-523 (Unprotected Transport of Credentials), CWE-539 (Use of Persistent Cookies Containing Sensitive Information)

⚠️ FINDING 1.3.2: Mensagem de erro revela método de autenticação tentado

  • Severidade: BAIXA
  • Localização: /data/user/0/com.openshell/files/rv-adv/supabase/functions/ti-webhook-receiver/index.ts:219-233
  • Descrição: Logs detalham qual método de autenticação falhou, o que pode ajudar atacantes a mapear a superfície de ataque
  • Evidência:
console.error(`[Webhook TI] Autenticação falhou. Método tentado: ${auth.method}`);
  • Recomendação: Reduzir detalhe em logs de produção:
// Em produção, logar apenas "Authentication failed" sem detalhes
const isProd = Deno.env.get("DENO_ENV") === "production";
console.error(isProd ? "[Webhook TI] Authentication failed" : `[Webhook TI] Auth failed: ${auth.method}`);
  • CWE: CWE-209 (Generation of Error Message Containing Sensitive Information)

1.3.4 Criptografia de Dados Sensíveis

2.1 Dados Pessoais (LGPD)

✅ FINDING 2.1.1: Criptografia implementada para senhas Meu INSS e Gov.br

  • Severidade: INFO
  • Localização: /data/user/0/com.openshell/files/rv-adv/supabase/migrations/043_encrypt_clients_passwords.sql
  • Descrição: Migration 043 implementa criptografia pgp_sym_encrypt para senha_meu_inss e senha_gov
  • Evidência:
ALTER TABLE public.clients ADD COLUMN IF NOT EXISTS senha_meu_inss_encrypted bytea;
ALTER TABLE public.clients ADD COLUMN IF NOT EXISTS senha_gov_encrypted bytea;

UPDATE public.clients
SET senha_meu_inss_encrypted = pgp_sym_encrypt(senha_meu_inss, encryption_key)
WHERE senha_meu_inss IS NOT NULL;
  • Recomendação: Manter e auditar acesso às funções de descriptografia
  • CWE: CWE-311 (Missing Encryption of Sensitive Data) - MITIGADO

⚠️ FINDING 2.1.2: Chave de criptografia compartilhada entre tabelas diferentes

  • Severidade: MÉDIA
  • Localização: /data/user/0/com.openshell/files/rv-adv/supabase/migrations/005_senha_inss_encryption.sql:22 e /data/user/0/com.openshell/files/rv-adv/supabase/migrations/043_encrypt_clients_passwords.sql:29
  • Descrição: Ambas migrations usam a mesma chave senha_inss_key do Vault para tabelas diferentes (pericias e clients)
  • Evidência:
-- Migration 005 (tabela pericias)
SELECT decrypted_secret FROM vault.decrypted_secrets WHERE name = 'senha_inss_key'

-- Migration 043 (tabela clients)  
SELECT decrypted_secret FROM vault.decrypted_secrets WHERE name = 'senha_inss_key'
  • Risco: Violação do princípio de separação de chaves. Se uma chave for comprometida, múltiplas tabelas são afetadas.
  • Recomendação:
    • Criar chaves separadas no Vault:
      • pericias_senha_inss_key
      • clients_senha_meu_inss_key
      • clients_senha_gov_key
  • CWE: CWE-321 (Use of Hard-coded Cryptographic Key)

⚠️ FINDING 2.1.3: Dados de saúde (benefícios) sem classificação explícita

  • Severidade: ALTA
  • Localização: N/A (schema)
  • Descrição: Não há evidência de classificação ou criptografia específica para dados de benefícios (saúde), que são dados sensíveis sob LGPD Art. 5º II
  • Evidência: Ausência de migrations ou políticas para dados de saúde
  • Recomendação:
    • Classificar dados de benefícios como "sensíveis" no schema
    • Implementar criptografia em repouso para campos de saúde
    • Adicionar RLS reforçado para dados de saúde
    • Documentar no docs/DATA_CLASSIFICATION.md
  • CWE: CWE-311 (Missing Encryption of Sensitive Data)

2.2 Campos Sensíveis Identificados

✅ FINDING 2.2.1: clients.senha_meu_inss e clients.senha_gov criptografados

  • Severidade: INFO
  • Localização: /data/user/0/com.openshell/files/rv-adv/supabase/migrations/043_encrypt_clients_passwords.sql
  • Descrição: Ambos campos possuem versões criptografadas (_encrypted)
  • Evidência:
ALTER TABLE public.clients ADD COLUMN IF NOT EXISTS senha_meu_inss_encrypted bytea;
ALTER TABLE public.clients ADD COLUMN IF NOT EXISTS senha_gov_encrypted bytea;
  • Recomendação: Manter e auditar acesso
  • CWE: CWE-311 - MITIGADO

⚠️ FINDING 2.2.2: clients.password pode estar em texto puro

  • Severidade: ALTA
  • Localização: /data/user/0/com.openshell/files/rv-adv/supabase/migrations/ (ausência)
  • Descrição: Não há migration que implemente criptografia para clients.password
  • Evidência: Ausência de migration para clients.password
  • Recomendação:
    • Verificar se clients.password é usado para autenticação (neste caso, deve usar hash bcrypt/argon2, não criptografia)
    • Se for senha de sistema externo, criptografar com pgp_sym_encrypt
  • CWE: CWE-311 (Missing Encryption of Sensitive Data)

⚠️ FINDING 2.2.3: client_inss_emails armazena conteúdo de e-mails sem criptografia

  • Severidade: ALTA
  • Localização: /data/user/0/com.openshell/files/rv-adv/supabase/functions/inss-webhook/index.ts:126-132
  • Descrição: E-mails do INSS contêm dados pessoais e são armazenados em texto puro (body_text)
  • Evidência:
const { data: insertedEmail, error: insertError } = await supabase
  .from('client_inss_emails')
  .insert({
    client_id: clientId,
    sender_address: from,
    subject: subject,
    body_text: text,  // ← Texto puro
    status: 'pendente'
  });
  • Risco: E-mails do INSS contêm CPF, números de benefício, dados de saúde
  • Recomendação:
    • Implementar criptografia para body_text e subject
    • Usar pgp_sym_encrypt com chave específica para e-mails
  • CWE: CWE-311 (Missing Encryption of Sensitive Data)

2.3 Transporte de Dados

✅ FINDING 2.3.1: Sem HTTP não seguro detectado no frontend

  • Severidade: INFO
  • Localização: /data/user/0/com.openshell/files/rv-adv/src/
  • Descrição: Busca por http:// (exceto localhost) não retornou resultados
  • Evidência: Comando grep -r "http://" src/ | grep -v "localhost" retornou vazio
  • Recomendação: Manter prática e adicionar lint rule para bloquear HTTP
  • CWE: CWE-319 (Cleartext Transmission of Sensitive Information) - MITIGADO

⚠️ FINDING 2.3.2: URL de scraper local em HTTP no .env.example

  • Severidade: BAIXA
  • Localização: /data/user/0/com.openshell/files/rv-adv/.env.example:109
  • Descrição: Variável VITE_SCRAPER_URL usa HTTP por padrão
  • Evidência:
VITE_SCRAPER_URL=http://localhost:3001
  • Recomendação: Adicionar comentário alertando para usar HTTPS em produção:
# Em produção, usar HTTPS com autenticação adequada
VITE_SCRAPER_URL=https://scraper.seudominio.com
  • CWE: CWE-319 - MITIGADO (apenas localhost)

📊 Resumo das Não Conformidades

SeveridadeCountAção Imediata
CRÍTICA0-
ALTA3Corrigir em até 7 dias
MÉDIA3Corrigir em até 30 dias
BAIXA3Corrigir em até 90 dias
INFO5Manter boas práticas

🎯 Plano de Ação Prioritário

Prioridade 1 (ALTA - 7 dias)

  1. FINDING 1.3.1: Remover fallback de query token em ti-webhook-receiver
  2. FINDING 2.1.3: Classificar e criptografar dados de saúde/benefícios
  3. FINDING 2.2.2: Verificar e corrigir clients.password
  4. FINDING 2.2.3: Criptografar conteúdo de e-mails em client_inss_emails

Prioridade 2 (MÉDIA - 30 dias)

  1. FINDING 1.1.2: Implementar validação de complexidade de segredos
  2. FINDING 2.1.2: Separar chaves de criptografia por tabela/finalidade
  3. FINDING 1.1.3: Documentar política de rotação de segredos

Prioridade 3 (BAIXA - 90 dias)

  1. FINDING 1.3.2: Reduzir detalhe em logs de autenticação
  2. FINDING 2.3.2: Atualizar comentário no .env.example

📚 Referências

  • Supabase Security Best Practices: /data/user/0/com.openshell/files/home/supabase-skills/skills/supabase/SKILL.md
  • Postgres RLS Guidelines: /data/user/0/com.openshell/files/home/supabase-skills/skills/supabase-postgres-best-practices/references/security-rls-basics.md
  • Postgres Privileges: /data/user/0/com.openshell/files/home/supabase-skills/skills/supabase-postgres-best-practices/references/security-privileges.md
  • CWE Database: https://cwe.mitre.org/

Relatório gerado em 2026-04-18 como parte da auditoria de segurança contínua.

Built with LogoFlowershow