Defesa em profundidade: Como proteger suas aplicações Next.js e Remix contra ataques SSRF (Server-Side Request Forgery)

Já algum tempo, as tecnologias frontend têm avançado em direção à se tornarem tecnologias full stack, demandando ao desenvolvedor conhecimento técnico tanto do browser quanto da aplicação sendo executada no servidor. Por conta disso alguns pontos de atenção nos vêm à tona, como por exemplo: ataques SSRF.

Foto de KeepCoding na Unsplash

Introdução – o que é SSRF?

SSRF (Server-Side Request Forgery) é uma vulnerabilidade em que um atacante consegue fazer com que o servidor da aplicação realize requisições HTTP arbitrárias. Isso é crítico porque transforma o servidor confiável em um agente malicioso, permitindo o acesso a recursos internos como:

  • Painéis administrativos internos (http://127.0.0.1/admin-status);
  • Serviços de metadados em nuvem (ex.: AWS IMDS em http://169.254.169.254/);
  • Outras APIs privadas que não deveriam ser expostas.

Em frameworks modernos como Next.js (nas API Routes) e Remix (em Loaders e Actions), o risco é real: qualquer lógica de servidor que aceite entrada do usuário e faça requisições externas pode ser explorada.


O Cenário de Risco (Exemplo de Código Vulnerável)

Imagine que você quer implementar um gerador de preview de links. O código abaixo parece inofensivo:

TypeScript
// exemplo vulnerável (Next.js API Route ou Remix Loader)
export async function loader({ request }) {
  const url = new URL(request.url).searchParams.get("target");

  // ❌ Risco: atacante controla a URL
  const response = await fetch(url);
  const html = await response.text();

  return { htmlPreview: html.substring(0, 200) };
}

Um atacante pode enviar:

TypeScript
/api/preview?target=http://127.0.0.1/admin-status

E, com isso, sua aplicação exporia informações internas que jamais deveriam sair do servidor.


Primeira Linha de Defesa: Whitelist de Hostnames

A primeira defesa contra SSRF é nunca confiar no input do usuário. Uma prática segura é permitir apenas domínios específicos (Whitelist).

TypeScript
const ALLOWED_HOSTS = ["example.com", "api.github.com"];

function isValidExternalUrl(urlString: string): boolean {
  try {
    const url = new URL(urlString);
    return ALLOWED_HOSTS.includes(url.hostname);
  } catch {
    return false;
  }
}
  • ✅ Segurança garantida contra destinos não previstos.
  • ❌ Limitação: não resolve sozinha truques de DNS ou redirecionamentos.

Segunda Linha de Defesa: Bloqueio de IP Privado

Mesmo validando o hostname, um atacante pode usar redirecionamentos ou subdomínios maliciosos para contornar a whitelist.

Por isso, é necessário resolver o DNS para um IP real e verificar se o endereço não pertence a redes privadas:

  • 10.0.0.0/8
  • 172.16.0.0/12
  • 192.168.0.0/16
  • 127.0.0.0/8 (loopback)

No Node.js, você pode usar bibliotecas como ipaddr.js, porém nada impede de você implementar uma função que valide IPs locais e/ou redes privadas. A título de exemplo, vamos seguir com esta lib:

TypeScript
import dns from "dns/promises";
import ipaddr from "ipaddr.js";

async function isSafeIp(hostname: string): Promise<boolean> {
  const addresses = await dns.lookup(hostname, { all: true });

  return addresses.every(addr => {
    const ip = ipaddr.parse(addr.address);
    if (ip.range() === "private" || ip.range() === "loopback") {
      return false;
    }
    return true;
  });
}

Essa segunda camada reduz drasticamente a superfície de ataque.


Nota Importante: Redirecionamentos Automáticos

Uma prática negligenciada é desabilitar o seguimento automático de redirecionamentos.
Atacantes podem hospedar um domínio aparentemente confiável que, ao ser acessado, redireciona para um IP privado.

No fetch, por exemplo, configure:

TypeScript
await fetch(url, { redirect: "manual" });

Isso adiciona mais uma barreira contra SSRF.


Conclusão e Próximos Passos

O SSRF é uma vulnerabilidade crítica porque transforma o servidor em um aliado do atacante.
Em aplicações Next.js e Remix, onde fetch é usado frequentemente em rotas de servidor, a atenção deve ser redobrada.

As camadas de proteção que você deve adotar:

  1. Whitelist de domínios confiáveis.
  2. Verificação de IPs privados.
  3. Bloqueio de redirecionamentos automáticos.

Defesa em profundidade garante que mesmo que uma camada falhe, outra estará presente para mitigar o risco.
A prevenção, neste caso, é muito mais barata (e segura) do que reagir a um incidente de segurança.


Referências