Information Disclosure em Erros da API

Só leitura — não altera nada no alvo. Só em alvo seu ou autorizado por escrito.

O vetor

Vetor real: servidores que vazam stack traces, nomes de arquivos, versões de bibliotecas, e detalhes internos em mensagens de erro dão ao invasor o mapa exato de como explorar. "E guilty of information disclosure" é uma das falhas mais comuns — e uma das primeiras coisas que um invasor procura. O que testamos: se a API vaza arquitetura, versões, ou detalhes internos em respostas de erro e em respostas normais.

Os passos

Passo 1 — Vazamento de arquitetura na resposta normal do feed:

BASE="https://seu-app.exemplo.workers.dev"
curl -s "$BASE/api/feed?limit=1" | python -c "
import sys, json
d = json.load(sys.stdin)
print('Campos visíveis:', list(d.keys()))
print('source:', d.get('source', 'não presente'))
"

O que esperar

Resultado esperado:

Campos visíveis: ['items', 'nextCursor', 'source']
source: database

🔍 O campo "source": "database" revela que os dados vêm do Supabase/Postgres, não de cache/KV. Informa ao invasor que atacar o banco afeta o feed diretamente.

Passo 2 — JSON malformado (erro verboso?):

curl -s -X POST "$BASE/api/feed" -H "Content-Type: application/json" -d 'INVALID{{{' -w "\nHTTP %{http_code}\n"

Resultado esperado: HTTP 405 (Method Not Allowed) — o endpoint só aceita GET. ✅ Bom — não vaza stack trace.

Passo 3 — Parâmetro absurdo (overflow):

curl -s "$BASE/api/feed?limit=999999999" | python -c "
import sys, json
d = json.load(sys.stdin)
print('Items retornados:', len(d.get('items', [])))
print('Source:', d.get('source'))
"

Resultado esperado: ~25 items (cap aplicado). ✅ Bom — mas source: "database" ainda vaza. O cap impede DoS mas não esconde a arquitetura.

Passo 4 — Headers de resposta (vazamento de versão):

curl -s -I "$BASE/api/feed" | grep -i "x-powered-by\|server\|x-nextjs\|via"

Resultado esperado: x-powered-by pode estar presente (depende da config). Se vazar Next.js ou versão, informa ao invasor qual CVE tentar.

Passo 5 — Erro 500 com path inexistente na API:

curl -s "$BASE/api/feed/nonexistent/deep/path" -w "\nHTTP %{http_code}\n" | head -3

Resultado esperado: HTTP 404 (Next.js not-found page) ou HTTP 200 (fallback). ✅ Não vaza stack trace.

Passo 6 — Erro de login com SQL-like injection (information disclosure):

curl -s -X POST "$BASE/api/admin/auth/login" \
  -H "Content-Type: application/json" \
  -d '{"email":"admin'\'' OR 1=1 --","password":"x"}' | head -3

Resultado esperado:

{"error": "Credenciais inválidas"}

✅ Bom — mensagem genérica, não diferencia "user not found" de "wrong password" no texto (mas difere no tempo — já coberto no Ex. 28).

Análise — o que isto significa:

  • "source": "database" no JSON é disclosure leve — não é crítico, mas informa ao invasor que não há cache intermediário.
  • Sem stack traces visíveis ✅ — a configuração de produção do Next.js suprime erros.
  • Headers não vazam versão ✅ — Cloudflare Workers não envia Server: nginx etc.
  • O disclosure principal é o x-powered-by (se presente) e o source field.

Como corrigir

Correção necessária (P2):

  1. Remover source da resposta — não expor se vem de DB ou cache:
    // Remover do response:
    // return Response.json({ items, nextCursor, source: 'database' });
    // Trocar por:
    return Response.json({ items, nextCursor });
    
  2. Remover x-powered-by: no next.config.ts:
    export default {
      poweredByHeader: false,
      // ...
    };
    
  3. Error handler global: criar app/api/error-handler.ts que sempre retorna { "error": "Erro interno" } sem detalhes, logando o erro real apenas no servidor.
  4. CSP object-src 'none' e base-uri 'self' para completar a CSP.

PurpleLab // plataforma de treino ofensivo + defensivo // Fase 1 — portal dinamico