Skip to main content

Como prevenir falsificação de solicitações do lado do servidor (SSRF) em aplicações Node.js

Escrito por

David Ekete

blog feature multithreading

20 de fevereiro de 2024

0 minutos de leitura
How to prevent SSRF Attacks in Node.js

A falsificação de solicitações do lado do servidor (SSRF) é uma vulnerabilidade comum que pode surgir sem que você perceba em qualquer aplicação Node.js. Ela representa uma ameaça significativa, pois os invasores podem manipular um servidor para fazer solicitações não pretendidas a recursos internos e externos.

Este artigo explica o que é SSRF, os riscos que ela pode trazer e as estratégias para mitigá-la em aplicações Node.js.

Muito recomendado: faça a lição interativa sobre falsificação de solicitações do lado do servidor no Snyk Learn.

O que é SSRF?

A SSRF ocorre quando um invasor explora as solicitações enviadas pelo servidor de uma aplicação web ao adulterar entradas ou manipular URLs.

A adulteração de entradas é a modificação não autorizada de dados, muitas vezes fornecidos por usuários, para explorar vulnerabilidades em uma aplicação. Já as vulnerabilidades de confusão de URL envolvem alterar URLs para induzir um sistema a realizar ações não pretendidas, como acessar recursos não autorizados ou fazer solicitações maliciosas.

Sob a influência do invasor, o servidor faz solicitações às URLs manipuladas, que parecem ter sido iniciadas pelo próprio servidor, contornando assim as medidas de segurança habituais. Um ataque SSRF bem-sucedido pode dar ao invasor acesso a recursos internos, bancos de dados e arquivos, expondo dados confidenciais, como credenciais.

Por exemplo, em um dos incidentes de SSRF mais notórios, um ex-engenheiro de desenvolvimento de software da Amazon explorou uma vulnerabilidade SSRF que expôs 140.000 números de Social Security, 80.000 números de contas bancárias e 1 milhão de números de seguro social. A violação afetou 106 milhões de pessoas e gerou prejuízos de US$ 250 milhões.

Um exemplo simples de ataque SSRF

Para entender melhor as vulnerabilidades SSRF, veja um exemplo específico. Neste cenário, você está trabalhando em uma aplicação web que permite que os usuários insiram uma URL, recupera uma imagem dessa URL e a exibe para o usuário.

Se quiser acompanhar, clone este repositório do GitHub, que contém o código-fonte da aplicação web. Ela usa Express, um framework Node.js que simplifica a criação de serviços web. Também usa Axios, uma biblioteca HTTP que permite fazer solicitações pelo Node.js.

Para clonar o repositório usando Git, execute o seguinte comando:

git clone https://github.com/davidekete/ssrf.git

Observação: é necessário ter o Git instalado no seu sistema para executar este comando.

Depois de clonar o repositório do GitHub, execute o seguinte comando para instalar as dependências:

npm install

Observação: é necessário ter o Node.js e o npm instalados para executar este comando.

A aplicação tem três rotas:

  • /upload-image-url permite que um usuário envie uma imagem usando a URL. Ela é acessível publicamente e, à primeira vista, parece inofensiva.

  • /users retorna todas as informações dos usuários da sua aplicação. Essa rota não deve ser acessível publicamente.

  • /users/:id/profile retorna o perfil de um usuário específico. Essa rota não deve ser acessível publicamente.

Inicie a aplicação com o seguinte comando:

node index.js

Ao fazer uma solicitação usando o Insomnia (ou qualquer outra ferramenta de teste de API) para /upload-image-url com uma URL de imagem válida, você deve receber a seguinte resposta:

Cliente de API exibindo uma solicitação de upload de imagem por URL para localhost e uma resposta 200 OK com HTML retornado pelo Imgur

Quando usuários mal-intencionados recebem essa resposta, sabem que seu servidor faz chamadas de API internamente usando a URL fornecida. Com essa informação, podem testar URLs diferentes para descobrir a estrutura interna do servidor ou enviar URLs como http://169.254.169.254 ou http://instance-data para acessar os metadados do Amazon Web Services (AWS) da sua aplicação, se ela estiver sendo executada em uma instância do Amazon Elastic Compute Cloud (Amazon EC2). Esse tipo de SSRF é conhecido como SSRF básica.

A SSRF básica ocorre quando um invasor manipula o servidor para que ele faça solicitações a recursos não pretendidos, muitas vezes sem conhecer diretamente a estrutura interna do servidor. Nesse caso, o invasor geralmente consegue ver a resposta à solicitação.

Outro tipo de SSRF é a SSRF cega, em que o invasor faz solicitações SSRF, mas não tem acesso direto à resposta do servidor. Em vez disso, ele se baseia no comportamento da aplicação para confirmar indiretamente o sucesso do ataque SSRF.

Por exemplo, se um invasor decidir testar a URL localhost:8050/users, receberá a seguinte resposta (ou seja, uma SSRF básica), que expõe os dados de todos os seus usuários:

Cliente de API mostrando uma solicitação POST para enviar uma imagem de uma URL e uma resposta 200 OK com os dados JSON retornados

Seu código está vulnerável a ataques SSRF porque aceita URLs fornecidas por usuários sem validação adequada ou controle de acesso, permitindo que eles façam solicitações a recursos internos ou privados potencialmente confidenciais. Além disso, não há nenhum mecanismo de lista de permissões ou de bloqueio para domínios específicos.

Para mitigar essa vulnerabilidade, seu código deve higienizar as entradas dos usuários, restringir o acesso a domínios confiáveis, usar firewalls e adotar práticas adequadas de tratamento de erros e monitoramento.

Como prevenir vulnerabilidades SSRF em Node.js

Há algumas opções para prevenir vulnerabilidades SSRF na sua aplicação Node.js. Na próxima seção, você vai conhecer alguns métodos e práticas recomendadas que podem ajudar.

Use as versões mais recentes das bibliotecas

Usar as versões mais recentes das bibliotecas é uma parte importante para reforçar a segurança da sua aplicação. As novas versões costumam incluir correções de segurança críticas e correções de bugs, que podem deixar sua aplicação menos vulnerável a ataques comuns, como SSRF.

No entanto, é essencial levar em conta problemas de compatibilidade, mudanças incompatíveis e compatibilidade com versões anteriores ao fazer atualizações. Fique atento aos ciclos de descontinuação, atualize grandes bases de código gradualmente e analise as dependências regularmente em busca de possíveis vulnerabilidades de segurança para manter uma aplicação segura e bem conservada.

Use o Snyk para atualizar bibliotecas e manter suas dependências atualizadas e livres de vulnerabilidades de segurança conhecidas publicamente.

Use um firewall

Um firewall é um sistema de segurança de rede que funciona como uma barreira de proteção e monitora e controla o tráfego de rede com base em regras predefinidas.

Para prevenir ataques SSRF com firewalls, você pode implantar um firewall de aplicações web (WAF) para inspecionar e bloquear solicitações maliciosas. Para isso, crie uma lista de permissões com domínios confiáveis e bloqueie URLs ou endereços IP suspeitos.

Higienize as entradas dos usuários

A higienização de entradas é uma prática de segurança que você pode usar para limpar e modificar os dados fornecidos pelos usuários, tornando-os seguros para processamento na sua aplicação. Seu principal objetivo é prevenir vulnerabilidades de segurança que podem surgir de entradas não confiáveis ou maliciosas.

Você pode prevenir SSRF higienizando todas as entradas dos usuários para garantir que as URLs estejam formatadas corretamente e não contenham caracteres inesperados antes de processá-las na aplicação.

Embora a higienização de entradas ajude a garantir que as URLs estejam formatadas corretamente, ela não deve ser usada como única medida.

Restrinja os esquemas de URL

Outra forma de prevenir vulnerabilidades SSRF é restringir os esquemas de URL. Ao especificar os esquemas de URL permitidos na sua aplicação (por exemplo, HTTP e HTTPS), você pode impedir que os usuários insiram URLs com esquemas potencialmente perigosos ou indesejados (por exemplo, file:// e ftp://).

Para implementar isso na aplicação que você clonou antes, verifique explicitamente se a URL fornecida começa com um esquema permitido, geralmente http:// ou https://. Se a URL não seguir esses esquemas, você poderá rejeitá-la:

const validURLPattern = /^https?:\/\//;
const imageURL = req.body.imageURL;

if (!imageURL || !validURLPattern.test(imageURL)) {
  return res.status(400).send("Invalid or missing image URL");
}

Crie uma lista de permissões com domínios confiáveis

Criar uma lista de permissões com domínios confiáveis ajuda a prevenir vulnerabilidades SSRF, permitindo solicitações apenas a domínios específicos que sejam seguros e necessários para sua aplicação.

Para implementar isso na aplicação que você clonou antes, crie um array (em uma aplicação real, talvez seja necessário usar uma abordagem mais robusta) com os domínios que você quer considerar confiáveis e aos quais permite que sua aplicação faça solicitações. Esses domínios devem ser aqueles com os quais sua aplicação interage e que considera seguros.

Antes de fazer uma solicitação à URL fornecida pelo usuário, verifique se ela corresponde a algum dos domínios confiáveis da sua lista. Se não corresponder, bloqueie a solicitação. Depois de implementar isso, sua rota /upload-image-url deve ficar assim:

app.post("/upload-image-url", async (req, res) => {
  try {
    const trustedDomains = [
      "https://unsplash.com/",
      "https://example.com",
      "https://pexels.com/",
    ];

    const imageURL = req.body.imageURL;

    if (!imageURL) {
      return res.status(400).send("Image URL is required");
    }

    let isTrustedDomain = false;

    for (const domain of trustedDomains) {
      if (imageURL.startsWith(domain)) {
        isTrustedDomain = true;
        break;
      }
    }

    if (!isTrustedDomain) {
      return res.status(403).send("Access to this domain is not permitted.");
    }

    // Proceed with the request to the trusted domain.
    const image = await axios.get(imageURL);

    return res.json({
      response: "Image uploaded successfully",
      image: image.data,
    });
  } catch (error) {
    if (axios.isAxiosError(error)) {
      return res.status(error.response.status).send(error.response.data);
    }

    res.status(500).send("Something went wrong");
  }
});

Este código modifica a rota /upload-image-url ao adicionar um array de domínios permitidos, verificar se a URL informada pelo usuário está na lista de permissões e rejeitar a solicitação caso não esteja.

Use a extensão de IDE do Snyk para detectar vulnerabilidades

O Snyk é uma ferramenta de segurança popular que pode ajudar a identificar e corrigir vulnerabilidades, incluindo SSRF, em bibliotecas de código aberto. O Snyk oferece integrações com diversos ambientes de desenvolvimento e IDEs, incluindo o Visual Studio Code (VS Code).

Para instalar a extensão de IDE do Snyk no VS Code, acesse a visualização Extensões clicando no ícone quadrado na barra lateral esquerda ou pressionando Ctrl + Shift + X (ou Cmd + Shift + X no macOS):

Visualização de extensões do Visual Studio Code com uma busca por “Snyk” e os detalhes da extensão Snyk Security instalada

Pesquise por "Snyk" no Marketplace de Extensões e clique no botão Instalar ao lado da extensão do Snyk:

O Visual Studio Code exibe a página da extensão Snyk Security, com detalhes da análise de código e controles de instalação

Depois de instalar a extensão, você precisa autenticá-la com sua conta do Snyk. Se ainda não tiver uma conta do Snyk, crie uma agora. Clique no ícone do Snyk e entre com suas credenciais do Snyk.

Em seguida, abra no VS Code o repositório do projeto que você clonou. Você verá uma solicitação perguntando se o Snyk deve confiar nessas pastas. Clique em Confiar nas pastas e continuar:

Caixa de aviso do Snyk informa que a verificação pode executar código e recomenda verificar apenas pastas confiáveis, com opções para confiar nas pastas ou rejeitá-las.

Você verá linhas onduladas vermelhas em trechos do seu código que têm vulnerabilidades de segurança (ou seja, em axios.get na linha 19). Ao passar o cursor sobre a linha ondulada, será exibida uma janela explicando a vulnerabilidade.

Código JavaScript no VS Code com um alerta de segurança que identifica uma vulnerabilidade de SSRF em uma solicitação axios.get.

Como alternativa, você pode clicar no ícone do Snyk e selecionar CODE SECURITY para ver os problemas de segurança. Ao selecionar uma vulnerabilidade específica, o Snyk abre a barra lateral esquerda e apresenta exemplos de código para ajudar você a resolver o problema.

Painel de vulnerabilidades do Snyk Code destacando uma falha de falsificação de requisição no servidor em código Node.js, na qual uma solicitação do axios usa uma URL de imagem fornecida pelo usuário

Conclusão

Neste artigo, você aprendeu que é essencial proteger suas aplicações Node.js contra SSRF e conheceu algumas estratégias e práticas recomendadas que podem ajudar.

Ao usar as versões mais recentes das bibliotecas, restringir os esquemas de URL, implementar listas de permissões e usar ferramentas de segurança como o Snyk, você pode reduzir significativamente a probabilidade de vulnerabilidades SSRF nas suas aplicações. Além disso, ao integrar o Snyk ao seu fluxo de desenvolvimento, você pode detectar com eficiência SSRF e outras vulnerabilidades no código antes que cheguem à produção, reforçando seu código e protegendo seus sistemas contra possíveis ataques.

Não espere! Comece hoje mesmo a proteger suas aplicações com o Snyk!

Comece a resolver desafios de Capture the Flag

Aprenda a resolver desafios de Capture the Flag assistindo ao nosso workshop virtual introdutório sob demanda.

Leia mais

feature insights context
Blog

Os ataques autônomos já chegaram. A defesa precisa acompanhar o ritmo.

Os atacantes autônomos estão reduzindo o tempo disponível para a defesa. Saiba como a descoberta, a correção, a validação e a prevenção contínuas ajudam as equipes de segurança a acompanhar esse ritmo.

illustration hero ai
Blog

O furacão da IA chegou

A IA está acelerando tanto a criação de software quanto os ataques cibernéticos. As lideranças devem proteger agentes e código desde o início, aplicar controles em tempo de execução e validar as defesas de forma independente.

feature insights context
Blog

A prevenção é essencialmente um problema resolvido?

A prevenção em código gerado por agentes está resolvida do ponto de vista arquitetural — mas escolher controles que protejam a segurança sem desacelerar o desenvolvimento continua sendo um desafio.