SSRF (Server-Side Request Forgery) é uma das falhas mais subestimadas em apps que integram serviços externos. Um único campo "URL da imagem" pode virar leitura de metadados do IMDS na AWS, acesso a bancos internos e escalada dentro da VPC. Guia técnico com 18 vetores.
Server-Side Request Forgery — SSRF — é uma classe de vulnerabilidade que quase nenhum backend "sem HTTP" pensaria ter. O padrão é sempre o mesmo: seu servidor faz uma requisição HTTP na URL que o usuário escolheu, e essa URL aponta para um endereço que só o servidor consegue enxergar.
Em apps modernos rodando em nuvem, o custo de uma SSRF vai de "vazamento de credenciais IAM" a "acesso a bancos e cache internos" em segundos. Este guia mapeia como a falha nasce, os 18 vetores que ainda escapam de bibliotecas populares em 2026, e o que blindar antes de qualquer coisa.
Onde a SSRF nasce
O padrão canônico é qualquer funcionalidade onde o backend puxa um recurso externo baseado em input do usuário:
- Upload por URL ("cole o link da imagem de perfil")
- Webhooks configuráveis pelo cliente
- Preview de link (aquele card do Slack/WhatsApp)
- Importadores de CSV/XML/JSON remotos
- Encurtadores de URL
- Screenshot ou geração de PDF a partir de URL
- OpenGraph fetch para SEO interno
- Health checks configuráveis
Em todos, o app faz algo como:
$content = file_get_contents($_POST['url']);
E é aí que o problema começa.
O que atacantes miram
Em vez de uma URL pública, o atacante manda o servidor buscar recursos que só o próprio servidor alcança:
- AWS/GCP metadata:
http://169.254.169.254/latest/meta-data/iam/security-credentials/retorna credenciais IAM temporárias. Um vazamento aqui costuma ser o pior dia do time de segurança. - Localhost:
http://127.0.0.1:6379(Redis),http://127.0.0.1:9200(Elasticsearch),http://127.0.0.1:15672(RabbitMQ mgmt). Muitos serviços internos não pedem autenticação porque assumem que só o servidor os enxerga. - Redes privadas:
10.0.0.0/8,172.16.0.0/12,192.168.0.0/16— outros microserviços da VPC, bancos internos, admin panels. - file://: leitura de arquivos locais quando o HTTP client segue esquemas alternativos.
Os 18 vetores que ainda passam
Uma blocklist ingênua ("se começar com http://127, bloqueia") falha por dezenas de razões. Estes são os vetores que ainda passam em produção:
1. IPs em decimal
http://2130706433 = 127.0.0.1. Muitas libs HTTP normalizam antes de checar.
2. IPs em hexadecimal
http://0x7f000001 = 127.0.0.1. Idem.
3. IPs em octal
http://0177.0.0.1 = 127.0.0.1.
4. IPv6 mapeado
http://[::ffff:127.0.0.1] continua chegando em localhost.
5. IPv4 embutido em IPv6
http://[::7f00:1] = mesma coisa.
6. IPv6 abreviado
http://[::1] = localhost em IPv6. Blocklist só de IPv4 falha.
7. DNS rebinding
O atacante controla mal.example.com. Na primeira consulta DNS responde com IP público. Sua checagem passa. Na segunda consulta (feita pelo HTTP client depois), responde com 127.0.0.1. Bingo.
8. Redirect manipulado
Você valida a URL de entrada, mas o HTTP client segue redirects automaticamente. Um Location: http://127.0.0.1 no meio do caminho contorna sua validação.
9. URL parsers divergentes
Em http://[email protected]/, alguns parsers extraem evil.com como host; outros, 127.0.0.1. Se você valida com um parser e faz a request com outro, adivinha.
10. Encoding duplo
http://127.0.0.1 → http://%31%32%37.%30.%30.%31 → http://%2531%2532%2537... depende de quantas vezes seu código decodifica.
11. Nomes de host de curl-magic
localtest.me, xip.io, nip.io resolvem para IPs privados sem envolver DNS customizado.
12. Wildcard DNS público
127.0.0.1.nip.io → 127.0.0.1. Sua blocklist de hostnames não pega.
13. Esquemas alternativos
file://, gopher://, dict://, ldap://. O gopher:// é particularmente perigoso porque permite enviar bytes arbitrários para portas TCP internas.
14. FTP passive redirect
Alguns clients HTTP + FTP fazem bind em portas locais.
15. Cloudflare Tunnel abuse
Um domínio público que via CDN tunela até IP interno.
16. TXT records maliciosos
Alguns integradores fazem lookup de TXT antes do fetch. Ataques via envenenamento de TXT record.
17. IPv6 zone identifier
http://[fe80::1%25eth0] — o %25 é % URL-encoded, o eth0 é um zone identifier que pode fazer chegar em interfaces internas.
18. Header host injection
Você valida a URL mas o proxy interno usa o Host: header para roteamento, e o atacante injeta um Host arbitrário.
Como blindar (de verdade)
Blocklist não é a resposta. Allowlist é.
Passo 1: separe o cliente HTTP. Crie um wrapper único (SafeHttpClient) que TODA request server-side passa. Nada de file_get_contents ou curl_exec espalhados.
Passo 2: resolva DNS antes. Faça o lookup você mesmo, com timeout curto. Cheque o IP resolvido contra uma allowlist de faixas públicas. Só então passe o IP resolvido diretamente ao HTTP client (não o hostname), forçando o Host header a bater.
Passo 3: bloqueie schemes. Só http e https. Nada de file, gopher, dict, ftp, ldap, data.
Passo 4: siga redirects manualmente. Cada hop passa pela mesma validação, ou o request é abortado.
Passo 5: timeout agressivo. 3 a 5 segundos para conexão + total.
Passo 6: role banido de metadata IMDS. Na AWS, use IMDSv2 obrigatório em toda instância (HttpTokens=required). É o único jeito de eliminar o vetor "SSRF vira credencial IAM" no pior caso.
Como saber se você tem essa falha agora
Duas checagens rápidas para rodar hoje:
- Em qualquer endpoint que aceite URL, tente
http://169.254.169.254/latest/meta-data/. Se o backend retorna qualquer coisa que pareça metadata, você tem SSRF crítica. - Tente
http://127.0.0.1:22. Se o retorno bater na porta SSH (mesmo com erro), o backend está fazendo conexões internas.
Se qualquer uma das duas passar, isso é P0 hoje.
TL;DR para o time
- SSRF nasce em qualquer feature que faz o servidor buscar recurso externo por URL do usuário.
- Blocklist perde. Allowlist ganha.
- Um único wrapper HTTP centralizado é infraestrutura de segurança, não frescura.
- Se você está na AWS, IMDSv2 obrigatório em toda instância é a segunda linha de defesa contra o vazamento de credenciais.
A varredura da Varredura testa esses 18 vetores automaticamente em todo endpoint que aceita URL — inclusive DNS rebinding e redirect manipulado. Rode uma varredura no seu app para ver quais destes passam hoje.
Sua app em produção passa por essa análise?
Rode uma varredura autônoma em ~60 segundos. Sem cadastro.