monteirotf.com
Postura de Segurança Automatizada

RealScan (Auditor Externo de Segurança)

Scanner de postura web em Cloudflare Workers: recebe um domínio e devolve um relatório com nota de A a F sobre HTTPS, headers de segurança, DNS de e-mail, subdomínios que podem expor a origem e segredos no código entregue ao navegador.

Serverless na edge
Execução
Detectados e ocultados
Segredos no cliente
Privadas, loopback e metadados
Guarda anti-SSRF

O problema

Erros de configuração em headers HTTP, registros DNS (SPF/DMARC) e vazamentos de credenciais no lado do cliente eram difíceis de auditar continuamente de fora para dentro.

A abordagem

Criei o RealScan, um scanner serverless em Cloudflare Workers que audita domínios de fora para dentro, checando headers, registros de e-mail e segredos expostos no cliente. A guarda anti-SSRF nega faixas privadas, loopback, link-local (incluindo o endpoint de metadados de nuvem), CGNAT e IPv4 escondido em IPv6.

01

Arquitetura

Cliente → Worker (index.js): POST { url }Worker (index.js) → ScanLimiter DO: IP do clienteWorker (index.js) → ssrf.js: URL após o limitessrf.js → DoH 1.1.1.1: resolve o hostssrf.js → scan.js: host validadoscan.js → Site analisado: fetches limitadosscan.js → DoH 1.1.1.1: SPF, DMARC, DNSSEC, CAAWorker (index.js) → Reports DO: waitUntil /saveCliente · formulário ou embed do portfólioClienteformulário ou embed do po…Worker (index.js) · POST /api/scan, /r/:id, /api/statsWorker (index.js)POST /api/scan, /r/:id, /…ScanLimiter DO · 20 scans por 10 min por IPScanLimiter DO20 scans por 10 min por IPssrf.js · só http/https, portas padrão, sem IP literalssrf.jssó http/https, portas pad…DoH 1.1.1.1 · A/AAAA, TXT, DNSSEC, CAADoH 1.1.1.1A/AAAA, TXT, DNSSEC, CAAscan.js · checagens isoladas, fetch com timeout e limite de bytesscan.jschecagens isoladas, fetch…Site analisado · HTML e até 6 scripts first-partySite analisadoHTML e até 6 scripts firs…Reports DO · relatório por id curto, expira em 30 diasReports DOrelatório por id curto, e…
  1. 01Cliente → Worker (index.js): POST { url }
  2. 02Worker (index.js) → ScanLimiter DO: IP do cliente
  3. 03Worker (index.js) → ssrf.js: URL após o limite
  4. 04ssrf.js → DoH 1.1.1.1: resolve o host
  5. 05ssrf.js → scan.js: host validado
  6. 06scan.js → Site analisado: fetches limitados
  7. 07scan.js → DoH 1.1.1.1: SPF, DMARC, DNSSEC, CAA
  8. 08Worker (index.js) → Reports DO: waitUntil /save
02

Decisões

  1. 01

    Regra de honestidade: só o que um Worker consegue verificar

    Por quê Um Worker não consegue fazer um handshake TLS bruto contra um host arbitrário nem descobrir de verdade a origem atrás de uma CDN. Então fiz o RealScan não fingir que faz.

    Custo Não há inspeção de certificado, e a detecção de subdomínios que expõem a origem é heurística e aparece rotulada como tal.

  2. 02

    Guarda anti-SSRF que resolve o host antes do scan

    Por quê Buscar URLs do usuário é um vetor de SSRF por padrão. O ssrf.js aceita só http/https nas portas 80/443, recusa IP literal e nomes internos, resolve A/AAAA e rejeita se algum endereço for privado ou reservado: RFC1918, loopback, link-local com 169.254.169.254, CGNAT, ULA, IPv4 mapeado em IPv6 e multicast.

    Custo Alvos por IP ou em portas fora do padrão não podem ser analisados.

  3. 03

    Rate limit antes de qualquer trabalho de saída

    Por quê O scanner age em nome de quem chama, então o limite serve para impedir o uso do RealScan contra terceiros, não para UX. Roda antes do DoH e do scan; só uma checagem sintática vem antes, para um erro de digitação não ser mascarado pelo limite.

    Custo Se o Durable Object do limite falhar, o scan segue sem limite (falha aberta).

  4. 04

    Cada checagem isolada

    Por quê Uma sonda que falha vira "não foi possível determinar" em vez de quebrar o relatório. O HTML do relatório escapa tudo o que vem do site analisado.

  5. 05

    Leituras com teto de bytes e de scripts

    Por quê Tratei esses valores como limites de memória e timeouts: o HTML é lido até 600000 bytes, cada script até 400000 bytes, e só os 6 primeiros scripts first-party são baixados para a busca de segredos. Segredos encontrados saem mascarados (4 primeiros e 4 últimos caracteres).

    Custo Conteúdo além do teto, scripts de terceiros e scripts a partir do sétimo não são examinados.

03

Números com fonte

Cada número aponta para o arquivo do repositório de onde saiu.

Scans por IP por janela
20 a cada 10 min (600000 ms) / 20 per 10 min (600000 ms)
fonte: wrangler.toml, src/config.js
Timeout de cada fetch
8000 ms
fonte: wrangler.toml, src/config.js
Timeout de DoH
6000 ms
fonte: wrangler.toml, src/config.js
Teto de leitura do HTML
600000 bytes
fonte: wrangler.toml, src/config.js
Teto de leitura por script
400000 bytes
fonte: wrangler.toml, src/config.js
Scripts first-party analisados
até 6 / up to 6
fonte: wrangler.toml, src/scan.js
Padrões de segredo
7 (AWS, Google, Stripe secret e publishable, Slack, GitHub, chave privada) / 7 (AWS, Google, Stripe secret and publishable, Slack, GitHub, private key)
fonte: src/scan.js
Subdomínios testados para exposição de origem
8
fonte: src/scan.js
Custo por achado (alto / médio / baixo)
20 / 10 / 4, a partir de 100 / from 100
fonte: wrangler.toml, src/scan.js
Notas
A >= 90, B >= 75, C >= 60, D >= 40, F abaixo / F below
fonte: wrangler.toml, src/config.js
Validade do relatório compartilhável
30 dias / 30 days
fonte: src/reports.js
04

Stack

  • Cloudflare Workers
  • JavaScript
  • DNS-over-HTTPS