📋 Relatório de Auditoria de Segurança - Webhooks e APIs

1.1.3 Webhooks e Autenticação

✅ ti-webhook-receiver/index.ts

AspectoStatusLocal
HMAC-SHA256✅ OKverifyHMAC() (l. 98-122) via Web Crypto API
Timing-safe comparison✅ OKtimingSafeEqual() custom (l. 79-93)
Multi-auth (3 métodos)✅ OKHMAC > Bearer > Query (l. 144-182)
Rate limiting✅ OK30 req/min (l. 195-196)
service_role✅ OKApenas interno, env var (l. 316-317)

⚠️ Risco Baixo: Query token pode vazar em logs/URLs (l. 169-176). Considerar deprecar.

✅ inss-webhook/index.ts

AspectoStatusLocal
HMAC-SHA256✅ OKverifyHMAC() (l. 9-23)
Timing-safe✅ OKWeb Crypto API nativa (mais seguro)
Rate limiting✅ OK5 req/min (l. 42-43)
Signature obrigatória✅ OKX-CF-Signature validado (l. 71-86)
service_role exclusivo✅ OKConfirma uso apenas aqui

🔴 Edge Functions - APIs Externas

FunctionAutenticaçãoProblema
ai-proxy✅ JWT (_shared/auth.ts)✅ Seguro
datajud-bypass✅ JWT (_shared/auth.ts)✅ Seguro
djen-bypass✅ JWT + rate limit interno⚠️ Rate limit duplicado
scrape-tnu✅ JWT (Header Bearer, l. 319-326)✅ Apropriado
🔴 delete-google-calendarVAZIO - CORS *🔴 CRÍTICO
🔴 sync-google-calendarVAZIO - sem JWT🔴 CRÍTICO

ERRO CRÍTICO: delete-google-calendar (l. 8) tem CORS aberto e não autentica nenhuma requisição. Qualquer site pode deletar eventos!


1.1.4 Rate Limiting

✅ _shared/rate-limit.ts (l. 1-58)

  • Implementação: In-Memory Map (isolate-scoped)
  • ✅ Headers X-RateLimit-* corretos
  • ✅ Limite de janela: 60 segundos
  • ⚠️ Limitação: Reseta em cold starts (sem persistência)

Uso por Function

FunctionLimiteStatus
ti-webhook-receiver30 req/min✅ OK
inss-webhook5 req/min✅ OK
import-tramita-clients3 req/min✅ OK
djen-bypass100 req/min (duplicado)⚠️ Usa própria implementação
ocr-classify-documentNENHUM🔴 Alto Risco
sync-google-calendarNENHUM🔴 Alto Risco
delete-google-calendarNENHUM🔴 Alto Risco
chat-jurisprudenciaNENHUM🟡 Médio Risco
generate-embeddingNENHUM🟡 Médio Risco

📊 Resumo de Problemas

SeveridadeProblemaArquivoCorreção
🔴 CRÍTICOSem autenticação + CORS abertodelete-google-calendar/index.ts:8Adicionar authenticateRequest + CORS restrito
🔴 CRÍTICOSem autenticação JWTsync-google-calendar/index.tsAdicionar authenticateRequest
🟡 AltoRate limit inconsistentedjen-bypass/index.ts:56-96Usar _shared/rate-limit.ts
🟡 AltoSem rate limitingocr-classify-document, sync-google-calendarAdicionar enforceRateLimit

✅ Comandos de Verificação (Executados)

ComandoResultado
ls supabase/functions/ | grep webhook2 webhooks: ti-webhook-receiver, inss-webhook
Buscar HMACti-webhook-receiver/l.98, inss-webhook/l.9
Buscar timingSafeEqualti-webhook-receiver/l.79
Buscar enforceRateLimit✅ 4 functions (ti, inss, import-tramita, djen usa próprio)
Buscar X-RateLimit✅ Headers corretos em todas com rate limit

📋 Recomendações Prioritárias

  1. IMEDIATO: Adicionar autenticação em delete-google-calendar e sync-google-calendar
  2. URGENTE: Adicionar rate limiting em todas as functions do Google Calendar (chatam API externa)
  3. IMPORTANTE: Unificar rate limiting para usar _shared/rate-limit.ts em todas
  4. CONSIDERAR: Migrar para Redis-backed rate limiting para persistência cross-isolate

[thinking] Agora tenho todas as informações necessárias para fazer a auditoria completa. Vou compilar um relatório detalhado baseado nas boas práticas do Supabase que li no SKILL.md.

📋 Relatório de Auditoria de Segurança - Webhooks e Rate Limiting

1.1.3 Webhooks e Autenticação de APIs

ti-webhook-receiver/index.ts (Linhas 1-369)

AspectoStatusDetalhes
HMAC-SHA256Implementado CorretamenteverifyHMAC() (l. 98-122) usa Web Crypto API com HMAC-SHA256
Timing-safe comparisonImplementadotimingSafeEqual() custom (l. 79-93) faz XOR bit-a-bit
Rate limitingImplementado30 req/min via _shared/rate-limit.ts (l. 195-196)
Multi-auth3 métodosHMAC-SHA256 > Bearer > Query token (l. 144-182)
service_role keyApenas internoUsado apenas server-side (l. 315-317)

⚠️ Problemas Identificados:

  1. Query token exposto em logs (l. 171-175) - O token pode vazar se o header X-Webhook-Signature ou Authorization não estiverem presentes (query params aparecem em URLs de logs)
  2. Sem verificação de secret em cache: Se TI_WEBHOOK_SECRET estiver vazio, retorna erro imediato (l. 139-142) - OK
  3. Nenhum método auth → 401 (l. 178-184) ✅ Comportamento correto

✅ Implementações Seguras:

  • Raw body usado antes do parse para HMAC (l. 213-214)
  • Log estruturado com método tentado (l. 219-225)
  • Idempotency Key logado (l. 207-210)

inss-webhook/index.ts (Linhas 1-288)

AspectoStatusDetalhes
HMAC-SHA256Implementado CorretamenteSame verifyHMAC() (l. 9-23)
Timing-safe validationImplementadoVia Web Crypto API nativa
Rate limitingImplementado5 req/min (l. 42-43)
Signature headerRequeridoX-CF-Signature (l. 69-86)
service_role keyApenas aquiConfirma exclusividade (l. 65)

⚠️ Observações:

  1. Sem timingSafeEqual custom - Usa Web Crypto API diretamente (recomendado e mais seguro)
  2. Timeout 30s no Gemini (l. 176) - Adequado
  3. CORS para OPTIONS (l. 37-39) - OK

✅ Segurança Excepcional:

  • Fall-through da IA testado (try/catch em Gemini com fallback implícito)
  • Logging estruturado de falhas de extração (l. 230-266)

🔍 Outros Webhooks Identificados:

WebhookArquivoAutenticaçãoStatus
ti-webhook-receiver/index.tsHMAC/Bearer/Query✅ Auditado
inss-webhook/index.tsHMAC (X-CF-Signature)✅ Auditado

Não há outros webhooks - As demais functions são APIs REST normais, não webhooks.


🔑 Edge Functions - APIs Externas:

FunctionAutenticaçãoAPI Key HandlingStatus
ai-proxy (l. 505-514)JWT via _shared/auth.ts✅ Chaves em env, nunca expostas✅ Seguro
datajud-bypass (l. 64-73)JWT via _shared/auth.tsAPIKey env (DATAJUD_API_KEY)✅ Seguro
djen-bypass (l. 69-79)JWT + Rate Limit (l. 82-96)API pública, sem key✅ Seguro (rate limit protege)

⚠️ NOTA CRÍTICA - delete-google-calendar/index.ts (l. 1-63):

  • Access-Control-Allow-Origin: * (l. 8) - ❌ CORS permissivo demais
  • Sem JWT authentication - Apenas verifica GOOGLE_CALENDAR_ACCESS_TOKEN (l. 28-34)
  • Vulnerável a CSRF - Qualquer site pode chamar

1.1.4 Rate Limiting

supabase/functions/_shared/rate-limit.ts (Linhas 1-58)

AspectoStatusDetalhes
ImplementaçãoIn-Memory Mapconst rateLimits = new Map<...>() (l. 6)
Isolate-scopedConfirmadoDocumentado nos comentários (l. 4-5)
Headers padronizadosImplementadoX-RateLimit-* (l. 27-29, 53-55)
Janela de tempo60 segundoswindowMs = 60000 (l. 15)

⚠️ Limitações Documentadas (comentários):

  • ✅ In-memory = reset em cada deploy/cold start
  • Não compartilhado entre isolates (o Supabase Edge Functions roda em múltiplos isolados)

Uso por Function:

FunctionLimiteImplementação
ti-webhook-receiver30 req/minenforceRateLimit(req, origin, 30) (l. 195)
inss-webhook5 req/minenforceRateLimit(req, origin, 5) (l. 42)
import-tramita-clients3 req/minenforceRateLimit(req, origin, 3) (l. 258)
djen-bypass100 req/minRate limit interno (l. 56) ou _shared?
ocr-classify-documentNenhumSem rate limit
sync-google-calendarNenhumSem rate limit
delete-google-calendarNenhumSem rate limit

⚠️ Problemas:

  1. Rate limit inconsistente: djen-bypass implementa seu próprio rate limit (l. 56-96) em vez de usar _shared/rate-limit.ts
  2. Funções do Google Calendar sem rate limit: sync-google-calendar, delete-google-calendar (ambas usam GOOGLE_CALENDAR_ACCESS_TOKEN fixo!)
  3. Sem Redis/backend: In-memory é suscetível a restart do Edge Runtime

📊 Resumo dos Problemas Críticos

SeveridadeProblemaLocalizaçãoCorreção
🔴 CRÍTICOdelete-google-calendar - CORS aberto, sem authdelete-google-calendar/index.ts:8Adicionar JWT auth + CORS restrito
🔴 CRÍTICOsync-google-calendar - sem autenticação JWTsync-google-calendar/index.tsAdicionar authenticateRequest
🟡 Altodjen-bypass - rate limit duplicado/inconsistentedjen-bypass/index.ts:56-96Usar _shared/rate-limit.ts
🟡 Altoocr-classify-document - sem rate limitocr-classify-document/index.tsAdicionar rate limiting
🟢 BaixoQuery token pode vazar em logsti-webhook-receiver/index.ts:171Considerar deprecar query param

✅ Comandos de Verificação (Executados)

# Listar todas as Edge Functions
ls supabase/functions/ | grep webhook  # 2 webhooks encontrados

# Buscar HMAC implementation  
# ✅ ti-webhook-receiver: verifyHMAC (l. 98)
# ✅ inss-webhook: verifyHMAC (l. 9)

# Buscar timing-safe comparison  
# ✅ ti-webhook-receiver: timingSafeEqual (l. 79)
# ✅ _shared/auth.ts: JWT verification com jose library

# Buscar rate limit
# ✅ 4 functions usando enforceRateLimit
# ⚠️ 2 functions sem rate limit (google-calendar-*)

# X-RateLimit headers
# ✅ todos que usam enforceRateLimit retornam headers corretos

Recomendação: Implementar rate limiting via Redis ou Supabase Rate Limiting service para

Built with LogoFlowershow