📋 Relatório de Auditoria de Segurança - Webhooks e APIs
📋 Relatório de Auditoria de Segurança - Webhooks e APIs
1.1.3 Webhooks e Autenticação
✅ ti-webhook-receiver/index.ts
| Aspecto | Status | Local |
|---|---|---|
| HMAC-SHA256 | ✅ OK | verifyHMAC() (l. 98-122) via Web Crypto API |
| Timing-safe comparison | ✅ OK | timingSafeEqual() custom (l. 79-93) |
| Multi-auth (3 métodos) | ✅ OK | HMAC > Bearer > Query (l. 144-182) |
| Rate limiting | ✅ OK | 30 req/min (l. 195-196) |
| service_role | ✅ OK | Apenas interno, env var (l. 316-317) |
⚠️ Risco Baixo: Query token pode vazar em logs/URLs (l. 169-176). Considerar deprecar.
✅ inss-webhook/index.ts
| Aspecto | Status | Local |
|---|---|---|
| HMAC-SHA256 | ✅ OK | verifyHMAC() (l. 9-23) |
| Timing-safe | ✅ OK | Web Crypto API nativa (mais seguro) |
| Rate limiting | ✅ OK | 5 req/min (l. 42-43) |
| Signature obrigatória | ✅ OK | X-CF-Signature validado (l. 71-86) |
| service_role exclusivo | ✅ OK | Confirma uso apenas aqui |
🔴 Edge Functions - APIs Externas
| Function | Autenticação | Problema |
|---|---|---|
| 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-calendar | ❌ VAZIO - CORS * | 🔴 CRÍTICO |
| 🔴 sync-google-calendar | ❌ VAZIO - 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
| Function | Limite | Status |
|---|---|---|
| ti-webhook-receiver | 30 req/min | ✅ OK |
| inss-webhook | 5 req/min | ✅ OK |
| import-tramita-clients | 3 req/min | ✅ OK |
| djen-bypass | 100 req/min (duplicado) | ⚠️ Usa própria implementação |
| ocr-classify-document | NENHUM | 🔴 Alto Risco |
| sync-google-calendar | NENHUM | 🔴 Alto Risco |
| delete-google-calendar | NENHUM | 🔴 Alto Risco |
| chat-jurisprudencia | NENHUM | 🟡 Médio Risco |
| generate-embedding | NENHUM | 🟡 Médio Risco |
📊 Resumo de Problemas
| Severidade | Problema | Arquivo | Correção |
|---|---|---|---|
| 🔴 CRÍTICO | Sem autenticação + CORS aberto | delete-google-calendar/index.ts:8 | Adicionar authenticateRequest + CORS restrito |
| 🔴 CRÍTICO | Sem autenticação JWT | sync-google-calendar/index.ts | Adicionar authenticateRequest |
| 🟡 Alto | Rate limit inconsistente | djen-bypass/index.ts:56-96 | Usar _shared/rate-limit.ts |
| 🟡 Alto | Sem rate limiting | ocr-classify-document, sync-google-calendar | Adicionar enforceRateLimit |
✅ Comandos de Verificação (Executados)
| Comando | Resultado |
|---|---|
ls supabase/functions/ | grep webhook | 2 webhooks: ti-webhook-receiver, inss-webhook |
| Buscar HMAC | ✅ ti-webhook-receiver/l.98, inss-webhook/l.9 |
Buscar timingSafeEqual | ✅ ti-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
- IMEDIATO: Adicionar autenticação em
delete-google-calendaresync-google-calendar - URGENTE: Adicionar rate limiting em todas as functions do Google Calendar (chatam API externa)
- IMPORTANTE: Unificar rate limiting para usar
_shared/rate-limit.tsem todas - 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)
| Aspecto | Status | Detalhes |
|---|---|---|
| HMAC-SHA256 | ✅ Implementado Corretamente | verifyHMAC() (l. 98-122) usa Web Crypto API com HMAC-SHA256 |
| Timing-safe comparison | ✅ Implementado | timingSafeEqual() custom (l. 79-93) faz XOR bit-a-bit |
| Rate limiting | ✅ Implementado | 30 req/min via _shared/rate-limit.ts (l. 195-196) |
| Multi-auth | ✅ 3 métodos | HMAC-SHA256 > Bearer > Query token (l. 144-182) |
| service_role key | ✅ Apenas interno | Usado apenas server-side (l. 315-317) |
⚠️ Problemas Identificados:
- Query token exposto em logs (l. 171-175) - O token pode vazar se o header
X-Webhook-SignatureouAuthorizationnão estiverem presentes (query params aparecem em URLs de logs) - Sem verificação de secret em cache: Se TI_WEBHOOK_SECRET estiver vazio, retorna erro imediato (l. 139-142) - OK
- 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)
| Aspecto | Status | Detalhes |
|---|---|---|
| HMAC-SHA256 | ✅ Implementado Corretamente | Same verifyHMAC() (l. 9-23) |
| Timing-safe validation | ✅ Implementado | Via Web Crypto API nativa |
| Rate limiting | ✅ Implementado | 5 req/min (l. 42-43) |
| Signature header | ✅ Requerido | X-CF-Signature (l. 69-86) |
| service_role key | ✅ Apenas aqui | Confirma exclusividade (l. 65) |
⚠️ Observações:
- Sem timingSafeEqual custom - Usa Web Crypto API diretamente (recomendado e mais seguro)
- Timeout 30s no Gemini (l. 176) - Adequado
- 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:
| Webhook | Arquivo | Autenticação | Status |
|---|---|---|---|
ti-webhook-receiver | /index.ts | HMAC/Bearer/Query | ✅ Auditado |
inss-webhook | /index.ts | HMAC (X-CF-Signature) | ✅ Auditado |
Não há outros webhooks - As demais functions são APIs REST normais, não webhooks.
🔑 Edge Functions - APIs Externas:
| Function | Autenticação | API Key Handling | Status |
|---|---|---|---|
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.ts | APIKey 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)
| Aspecto | Status | Detalhes |
|---|---|---|
| Implementação | ✅ In-Memory Map | const rateLimits = new Map<...>() (l. 6) |
| Isolate-scoped | ✅ Confirmado | Documentado nos comentários (l. 4-5) |
| Headers padronizados | ✅ Implementado | X-RateLimit-* (l. 27-29, 53-55) |
| Janela de tempo | ✅ 60 segundos | windowMs = 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:
| Function | Limite | Implementação |
|---|---|---|
ti-webhook-receiver | 30 req/min | enforceRateLimit(req, origin, 30) (l. 195) |
inss-webhook | 5 req/min | enforceRateLimit(req, origin, 5) (l. 42) |
import-tramita-clients | 3 req/min | enforceRateLimit(req, origin, 3) (l. 258) |
djen-bypass | 100 req/min | Rate limit interno (l. 56) ou _shared? |
ocr-classify-document | Nenhum | ❌ Sem rate limit |
sync-google-calendar | Nenhum | ❌ Sem rate limit |
delete-google-calendar | Nenhum | ❌ Sem rate limit |
⚠️ Problemas:
- Rate limit inconsistente:
djen-bypassimplementa seu próprio rate limit (l. 56-96) em vez de usar_shared/rate-limit.ts - Funções do Google Calendar sem rate limit:
sync-google-calendar,delete-google-calendar(ambas usamGOOGLE_CALENDAR_ACCESS_TOKENfixo!) - Sem Redis/backend: In-memory é suscetível a restart do Edge Runtime
📊 Resumo dos Problemas Críticos
| Severidade | Problema | Localização | Correção |
|---|---|---|---|
| 🔴 CRÍTICO | delete-google-calendar - CORS aberto, sem auth | delete-google-calendar/index.ts:8 | Adicionar JWT auth + CORS restrito |
| 🔴 CRÍTICO | sync-google-calendar - sem autenticação JWT | sync-google-calendar/index.ts | Adicionar authenticateRequest |
| 🟡 Alto | djen-bypass - rate limit duplicado/inconsistente | djen-bypass/index.ts:56-96 | Usar _shared/rate-limit.ts |
| 🟡 Alto | ocr-classify-document - sem rate limit | ocr-classify-document/index.ts | Adicionar rate limiting |
| 🟢 Baixo | Query token pode vazar em logs | ti-webhook-receiver/index.ts:171 | Considerar 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