Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

SSRF no Node.js: Como Prevenir

Atualizado em: 30 de agosto de 2026

Rack de servidores processando fluxos de dados no Node.js

Prevenir SSRF no Node.js é essencial quando a aplicação acessa URLs fornecidas direta ou indiretamente pelo usuário. SSRF, ou Server-Side Request Forgery, permite que um atacante faça o servidor enviar requisições para destinos internos, serviços de metadata da cloud, painéis administrativos ou portas que não deveriam estar expostas.

O risco aparece em importadores de imagem, webhooks de teste, previews de links, proxies, leitores de feed, conversores de PDF e integrações que aceitam uma URL. Validar apenas o texto inicial não é suficiente, porque DNS, redirects, IPv6, URLs codificadas e respostas intermediárias podem levar a outro destino.

Neste guia, você aprenderá a usar allowlists, validar protocolos e portas, resolver DNS, bloquear redes privadas, controlar redirects, proteger metadata, limitar respostas e testar tentativas de bypass.

O que é SSRF?

SSRF ocorre quando um atacante influencia uma requisição feita pelo servidor. A referência SSRF da OWASP descreve o ataque. A SSRF Prevention Cheat Sheet reúne defesas.

Exemplo vulnerável

app.get('/preview', async (req, res) => {
  const response = await fetch(req.query.url);
  res.send(await response.text());
});

O cliente pode tentar destinos como localhost, serviços internos ou metadata da plataforma.

Impactos

  • leitura de serviços internos;
  • acesso a metadata e credenciais temporárias;
  • port scanning interno;
  • contorno de firewall;
  • chamadas a painéis administrativos;
  • exfiltração por redirects;
  • DoS com respostas grandes ou lentas.

Melhor defesa: allowlist

Quando possível, aceite apenas hosts conhecidos:

const allowedHosts = new Set([
  'images.example-cdn.com',
  'api.partner.example'
]);

function validateHost(url) {
  if (!allowedHosts.has(url.hostname)) {
    throw new Error('Host não permitido');
  }
}

Allowlist é mais segura que tentar bloquear todos os destinos perigosos.

Valide o protocolo

if (!['https:'].includes(url.protocol)) {
  throw new Error('Protocolo não permitido');
}

Não permita file:, ftp:, gopher: ou protocolos desconhecidos. Mesmo HTTP pode ser proibido quando HTTPS é obrigatório.

Valide a porta

const port = url.port || '443';

if (port !== '443') {
  throw new Error('Porta não permitida');
}

Uma URL HTTPS em porta arbitrária ainda pode alcançar serviços internos.

Credenciais na URL

if (url.username || url.password) {
  throw new Error('Credenciais na URL não são permitidas');
}

Evite URLs como https://user:pass@host, que podem confundir validações e vazar dados.

Normalização com URL

let parsed;
try {
  parsed = new URL(input);
} catch {
  throw new ValidationError('URL inválida');
}

Não valide com regex simples. A classe URL ajuda a interpretar hostname, protocolo e porta de forma consistente.

Localhost

Bloqueie nomes e endereços de loopback:

localhost
127.0.0.0/8
::1

Não basta bloquear a string 127.0.0.1. Existem representações alternativas e IPv6.

Redes privadas

Bloqueie faixas privadas e especiais:

  • 10.0.0.0/8;
  • 172.16.0.0/12;
  • 192.168.0.0/16;
  • 169.254.0.0/16;
  • 100.64.0.0/10;
  • faixas IPv6 locais e link-local;
  • multicast e endereços reservados.

Resolução DNS

import dns from 'node:dns/promises';

const addresses = await dns.lookup(hostname, {
  all: true,
  verbatim: true
});

Valide todos os endereços retornados. Um hostname pode resolver para IPv4 e IPv6.

DNS rebinding

Um domínio pode resolver primeiro para IP público e depois para IP privado. Validar uma resolução e deixar o cliente HTTP resolver novamente cria uma janela de ataque.

Fixando o endereço validado

Uma defesa avançada resolve o hostname, valida o IP e conecta exatamente ao endereço aprovado, mantendo o hostname para SNI e Host quando necessário. Use uma biblioteca ou agente que permita lookup controlado.

const agent = new Agent({
  connect: {
    lookup: safeLookup
  }
});

Teste cuidadosamente TLS, redirects e IPv6.

Redirects

Um destino público pode redirecionar para localhost. Desative redirects automáticos ou valide cada etapa:

const response = await fetch(url, {
  redirect: 'manual'
});

Validando cada redirect

  1. Receba o header Location.
  2. Resolva contra a URL atual.
  3. Valide protocolo, host, porta e IP.
  4. Limite a quantidade.
  5. Faça a próxima requisição.

Defina no máximo poucos redirects.

Metadata da cloud

Serviços de metadata costumam usar endereços link-local. Bloqueie a rede no código e na infraestrutura. Quando disponível, exija versões do serviço com token e desabilite acesso desnecessário.

Defesa de rede

O processo da aplicação não deve ter acesso irrestrito a toda a rede interna. Use egress firewall, network policies, service mesh ou proxy de saída.

Proxy de saída

Um egress proxy central pode aplicar allowlists, DNS seguro, logs e limites. A aplicação envia requisições externas apenas por esse componente.

Timeout

const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 3000);

try {
  return await fetch(url, { signal: controller.signal });
} finally {
  clearTimeout(timer);
}

Timeout reduz conexões presas, mas não corrige SSRF.

Limite de resposta

Não use response.text() sem limite em conteúdo arbitrário. Leia o stream e interrompa após o máximo permitido.

const MAX_BYTES = 2 * 1024 * 1024;

Content-Length

O header ajuda, mas pode estar ausente ou incorreto. Continue contando bytes durante a leitura.

Tipos de conteúdo

Se o recurso deve ser imagem, valide Content-Type e o formato real. Não confie apenas na extensão.

Compressão

Uma resposta pequena comprimida pode expandir enormemente. Defina limite após descompressão e proteja contra compression bombs.

Upload de imagem por URL

Baixe em um worker isolado, limite tamanho, dimensões e tempo, depois reencode a imagem. Não sirva o conteúdo bruto como HTML.

Extraia apenas metadados necessários. Não execute scripts, não carregue recursos secundários e não envie cookies da aplicação.

Webhooks de teste

Se o usuário configura um endpoint, considere permitir apenas HTTPS público e executar testes por uma infraestrutura de egress dedicada.

Headers

Não encaminhe Authorization, cookies, headers internos ou trace baggage para um destino controlado pelo usuário.

const headers = {
  'user-agent': 'PreviewBot/1.0',
  accept: 'text/html,application/xhtml+xml'
};

Host header

Não permita que o cliente escolha um Host diferente do destino validado. Isso pode alcançar virtual hosts internos.

Autenticação em URLs externas

Quando uma integração precisa de token, associe a credencial a um host fixo. Nunca envie um segredo para uma URL arbitrária.

Consulte Gestão de Segredos no Node.js.

Proteção contra redirects com segredo

Não encaminhe Authorization ao mudar de origin. Reavalie cada redirect e remova headers sensíveis.

URL assinada

URLs pré-assinadas podem conter credenciais na query. Não registre o valor e aceite apenas provedores e hosts esperados.

SSRF cego

Mesmo sem devolver a resposta, o atacante pode observar tempo, status indireto ou efeitos no serviço interno. Não considere seguro apenas porque o corpo não é mostrado.

Port scanning

Diferenças de timeout e erro revelam portas abertas. Padronize respostas e aplique allowlist.

Erros para o cliente

{
  "code": "URL_NOT_ALLOWED",
  "message": "Não foi possível acessar o endereço informado"
}

Não revele IP resolvido, porta interna ou motivo detalhado.

Logs

Registre host normalizado, categoria do bloqueio, duração e request ID. Não registre query completa quando pode conter tokens.

Veja Logs com Pino no Node.js.

Auditoria

Alterações de allowlist, tentativas repetidas contra rede privada e acessos a metadata devem ser auditados. Consulte Logs de Auditoria no Node.js.

Rate limiting

Limite requisições de preview, importação e teste de webhook. SSRF também pode ser usado para DoS.

Consulte Rate Limiting no Node.js.

Filas

Downloads externos podem ser executados em workers com concorrência e rede restritas. O job deve carregar apenas a URL e o tenant necessários.

Multi-tenancy

Allowlists específicas por tenant não devem permitir acesso a destinos internos. Valide regras globais antes das regras configuráveis.

Teste de loopback

const blocked = [
  'http://127.0.0.1',
  'http://[::1]',
  'http://localhost'
];

Todos devem falhar antes da conexão.

Teste de rede privada

Teste endereços de todas as faixas IPv4 e IPv6 proibidas.

Teste de redirect

Crie um servidor público de teste que redireciona para localhost e confirme o bloqueio.

Teste de DNS rebinding

Use um resolvedor controlado que devolve IP público e depois privado. A conexão não pode usar o segundo endereço sem nova validação.

Teste de resposta grande

Envie conteúdo além do limite e confirme cancelamento do stream.

Teste de timeout

Use um servidor que aceita a conexão e não responde. O worker deve liberar os recursos.

Erros comuns

  • Bloquear apenas localhost: IPs privados continuam acessíveis.
  • Validar antes do redirect: o destino final muda.
  • Validar DNS e resolver novamente: rebinding contorna a regra.
  • Permitir qualquer porta: serviços internos são alcançados.
  • Encaminhar Authorization: segredos vazam.
  • Sem limite de corpo: resposta causa DoS.
  • Confiar apenas no código: falta isolamento de rede.

Boas práticas

  • Prefira allowlist.
  • Permita apenas HTTPS.
  • Valide porta e credenciais.
  • Resolva e valide todos os IPs.
  • Controle redirects.
  • Bloqueie metadata e redes privadas.
  • Use egress firewall.
  • Limite tempo e tamanho.
  • Não encaminhe segredos.
  • Teste bypasses de DNS e IPv6.

Conclusão

Prevenir SSRF no Node.js exige validar o destino real, não apenas a string da URL. Protocolo, porta, DNS, IP e cada redirect precisam permanecer dentro das regras permitidas.

Allowlists, isolamento de rede, limites e headers mínimos formam uma defesa em profundidade. Quando downloads e previews usam um egress controlado, a aplicação pode acessar recursos externos sem transformar o servidor em uma ponte para sua infraestrutura interna.

10 melhores cursos de programação em 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita