🔒 Auditoria de Segurança: Webhooks e Criptografia de Dados Sensíveis
🔒 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
| Categoria | Críticas | Altas | Médias | Baixas | Info |
|---|---|---|---|---|---|
| Webhooks | 0 | 2 | 3 | 1 | 2 |
| Criptografia | 0 | 1 | 2 | 2 | 3 |
| TOTAL | 0 | 3 | 5 | 3 | 5 |
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
.envnunca 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:216e/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
timingSafeEqualprevine 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_inssesenha_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:22e/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_keydo Vault para tabelas diferentes (periciaseclients) - 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_keyclients_senha_meu_inss_keyclients_senha_gov_key
- Criar chaves separadas no Vault:
- 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
- Verificar se
- 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_textesubject - Usar
pgp_sym_encryptcom chave específica para e-mails
- Implementar criptografia para
- 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_URLusa 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
| Severidade | Count | Ação Imediata |
|---|---|---|
| CRÍTICA | 0 | - |
| ALTA | 3 | Corrigir em até 7 dias |
| MÉDIA | 3 | Corrigir em até 30 dias |
| BAIXA | 3 | Corrigir em até 90 dias |
| INFO | 5 | Manter boas práticas |
🎯 Plano de Ação Prioritário
Prioridade 1 (ALTA - 7 dias)
- FINDING 1.3.1: Remover fallback de query token em
ti-webhook-receiver - FINDING 2.1.3: Classificar e criptografar dados de saúde/benefícios
- FINDING 2.2.2: Verificar e corrigir
clients.password - FINDING 2.2.3: Criptografar conteúdo de e-mails em
client_inss_emails
Prioridade 2 (MÉDIA - 30 dias)
- FINDING 1.1.2: Implementar validação de complexidade de segredos
- FINDING 2.1.2: Separar chaves de criptografia por tabela/finalidade
- FINDING 1.1.3: Documentar política de rotação de segredos
Prioridade 3 (BAIXA - 90 dias)
- FINDING 1.3.2: Reduzir detalhe em logs de autenticação
- 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.