Skip to main content

Cómo prevenir la falsificación de solicitudes del lado del servidor en aplicaciones Node.js

Escrito por

David Ekete

blog feature multithreading

20 de febrero de 2024

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

La falsificación de solicitudes del lado del servidor (SSRF) es una vulnerabilidad común que puede aparecer inadvertidamente en cualquier aplicación Node.js. Representa una amenaza importante porque los atacantes pueden manipular un servidor para que realice solicitudes no deseadas a recursos internos y externos.

En este artículo exploraremos SSRF, sus posibles riesgos y las estrategias para mitigarla en aplicaciones Node.js.

Muy recomendado: Toma la lección interactiva sobre falsificación de solicitudes del lado del servidor en Snyk Learn.

¿Qué es SSRF?

La SSRF ocurre cuando un atacante aprovecha las solicitudes que envía el servidor de una aplicación web mediante la manipulación de entradas o de URL.

La manipulación de entradas consiste en modificar datos sin autorización, a menudo generados por usuarios, para aprovechar vulnerabilidades de una aplicación. En cambio, las vulnerabilidades por confusión de URL consisten en alterar URL para engañar a un sistema y hacer que realice acciones no deseadas, como acceder a recursos sin autorización o enviar solicitudes maliciosas.

Bajo la influencia del atacante, el servidor envía solicitudes a las URL manipuladas, que parecen haberse iniciado desde el propio servidor y, por lo tanto, evaden las medidas de seguridad habituales. Un ataque SSRF exitoso puede darle al atacante acceso a recursos internos, bases de datos y archivos, y exponer datos confidenciales, como credenciales.

Por ejemplo, en uno de los incidentes de SSRF más conocidos, un exingeniero de desarrollo de software de Amazon aprovechó una vulnerabilidad SSRF que expuso 140,000 números de Seguro Social, 80,000 números de cuentas bancarias y 1 millón de números de seguro social. La filtración afectó a 106 millones de personas y ocasionó daños por 250 millones de dólares estadounidenses.

Un ejemplo sencillo de ataque SSRF

Para comprender mejor las vulnerabilidades SSRF, veamos un ejemplo concreto. En este escenario, trabajas en una aplicación web que permite a los usuarios ingresar una URL, obtener una imagen desde esa URL y mostrarla.

Si quieres seguir los pasos, clona este repositorio de GitHub, que contiene el código fuente de la aplicación web. Esta aplicación usa Express, un framework de Node.js que simplifica la creación de servicios web. También usa Axios, una biblioteca HTTP que permite realizar solicitudes desde Node.js.

Para clonar el repositorio con Git, ejecuta el siguiente comando:

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

Nota: Debes tener Git instalado en tu sistema para ejecutar este comando.

Después de clonar el repositorio de GitHub, ejecuta el siguiente comando para instalar las dependencias:

npm install

Nota: Debes tener Node.js y npm instalados para ejecutar este comando.

La aplicación tiene tres rutas:

  • /upload-image-url permite que un usuario cargue una imagen mediante una URL. Es de acceso público y, a primera vista, no parece causar ningún daño.

  • /users devuelve toda la información de los usuarios de tu aplicación. No debería ser de acceso público.

  • /users/:id/profile devuelve el perfil de un usuario específico. No debería ser de acceso público.

Inicia la aplicación con el siguiente comando:

node index.js

Cuando envíes una solicitud con Insomnia (o cualquier otra herramienta para probar API) a /upload-image-url con una URL válida de imagen, deberías obtener la siguiente respuesta:

Cliente de API que muestra una solicitud para subir una URL de imagen a localhost y una respuesta 200 OK que contiene HTML devuelto por Imgur

Cuando usuarios maliciosos reciben esta respuesta, saben que tu servidor realiza llamadas a API internamente usando la URL proporcionada. Ahora pueden probar distintas URL para averiguar la estructura interna de tu servidor o enviar URL, como http://169.254.169.254 o http://instance-data, para acceder a los metadatos de Amazon Web Services (AWS) de tu aplicación si esta se ejecuta en una instancia de Amazon Elastic Compute Cloud (Amazon EC2). Este tipo de SSRF se conoce como SSRF básica.

La SSRF básica ocurre cuando un atacante manipula el servidor para que envíe solicitudes a recursos no deseados, a menudo sin conocer directamente la estructura interna del servidor. En este caso, el atacante generalmente puede ver la respuesta a la solicitud.

Otro tipo de SSRF es la SSRF ciega, en la que un atacante envía solicitudes SSRF, pero no tiene acceso directo a la respuesta del servidor. En su lugar, se basa en el comportamiento de la aplicación para confirmar indirectamente que el ataque SSRF tuvo éxito.

Por ejemplo, si un atacante decide probar la URL localhost:8050/users, recibirá la siguiente respuesta (es decir, una SSRF básica), que expone los datos de todos tus usuarios:

Cliente de API que muestra una solicitud POST para subir una imagen desde una URL y una respuesta 200 OK con datos JSON

Tu código es vulnerable a ataques SSRF porque acepta URL proporcionadas por los usuarios sin validarlas ni controlar el acceso adecuadamente. Esto permite que los usuarios envíen solicitudes a recursos internos o privados potencialmente confidenciales. Tampoco hay ningún mecanismo para permitir o bloquear dominios específicos mediante listas de permitidos o de bloqueados.

Para mitigar esta vulnerabilidad, tu código debe depurar las entradas de usuario, restringir el acceso a dominios de confianza, usar firewalls y establecer prácticas adecuadas de manejo de errores y monitoreo.

Cómo prevenir vulnerabilidades SSRF en Node.js

Si quieres prevenir vulnerabilidades SSRF en tu aplicación Node.js, tienes varias opciones. En la siguiente sección conocerás algunos métodos y prácticas recomendadas que pueden ayudarte.

Usa las versiones más recientes de las bibliotecas

Usar las versiones más recientes de las bibliotecas es importante para mejorar la seguridad de tu aplicación. Las nuevas versiones suelen incluir parches de seguridad esenciales y correcciones de errores, que pueden reducir la vulnerabilidad de tu aplicación ante ataques comunes, como SSRF.

Sin embargo, al actualizar, es fundamental tener en cuenta los problemas de compatibilidad, los cambios incompatibles y la compatibilidad con versiones anteriores. Presta atención a los ciclos de obsolescencia, actualiza gradualmente las bases de código grandes y analiza periódicamente las dependencias para detectar posibles vulnerabilidades de seguridad y mantener una aplicación segura y bien mantenida.

Usa Snyk para actualizar las bibliotecas y mantener tus dependencias al día y libres de vulnerabilidades de seguridad conocidas públicamente.

Usa un firewall

Un firewall es un sistema de seguridad de red que actúa como barrera de protección y monitorea y controla el tráfico de red según reglas predefinidas.

Para prevenir ataques SSRF con firewalls, puedes implementar un firewall de aplicaciones web (WAF) que inspeccione y bloquee solicitudes maliciosas. Para ello, crea una lista de permitidos con dominios de confianza y bloquea las URL o direcciones IP sospechosas.

Depura las entradas de usuario

La depuración de entradas es una práctica de seguridad que permite limpiar y modificar los datos proporcionados por los usuarios para que puedan procesarse de forma segura en tu aplicación. Su propósito principal es prevenir vulnerabilidades de seguridad que pueden surgir de entradas no confiables o maliciosas.

Puedes prevenir la SSRF depurando todas las entradas de usuario para asegurarte de que las URL tengan el formato correcto y no contengan caracteres inesperados antes de procesarlas en tu aplicación.

Aunque la depuración de entradas puede ayudar a garantizar que las URL tengan el formato correcto, no debes usarla de forma aislada.

Exige esquemas de URL

Otra forma de prevenir vulnerabilidades SSRF es exigir esquemas de URL específicos. Al definir los esquemas de URL permitidos en tu aplicación (p. ej., HTTP y HTTPS), puedes impedir que los usuarios ingresen URL con esquemas potencialmente peligrosos o no deseados (p. ej., file:// y ftp://).

Para implementar esto en la aplicación que clonaste antes, puedes verificar explícitamente que la URL proporcionada comience con un esquema permitido, normalmente http:// o https://. Si la URL no cumple con estos esquemas, puedes rechazarla:

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

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

Crea una lista de permitidos con dominios de confianza

Crear una lista de permitidos con dominios de confianza previene las vulnerabilidades SSRF al permitir solicitudes únicamente a dominios específicos que se sabe que son seguros y necesarios para tu aplicación.

Para implementarlo en la aplicación que clonaste antes, crea un arreglo (en una aplicación real, quizá necesites un método más sólido) con los dominios en los que confías y a los que permites que tu aplicación envíe solicitudes. Deben ser dominios con los que tu aplicación interactúe y que considere seguros.

Luego, antes de solicitar la URL proporcionada por el usuario, verifica si coincide con alguno de los dominios de confianza de tu lista. Si no coincide, bloquea la solicitud. Después de implementarlo, tu ruta /upload-image-url debería verse así:

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 la ruta /upload-image-url al agregar un arreglo de dominios permitidos, verificar si la URL proporcionada por el usuario forma parte de la lista y rechazar la solicitud si la URL no está incluida.

Usa la extensión de Snyk para IDE para detectar vulnerabilidades

Snyk es una herramienta de seguridad popular que puede ayudar a identificar y corregir vulnerabilidades, incluida la SSRF, en bibliotecas de código abierto. Snyk ofrece integraciones con distintos entornos de desarrollo e IDE, incluido Visual Studio Code (VS Code).

Para instalar la extensión de Snyk para IDE en VS Code, abre la vista Extensiones haciendo clic en el ícono cuadrado de la barra lateral izquierda o presionando Ctrl + Shift + X (o Cmd + Shift + X en macOS):

Vista de extensiones de Visual Studio Code que muestra una búsqueda de “Snyk” y los detalles de la extensión Snyk Security instalada

Busca "Snyk" en Marketplace de extensiones y haz clic en el botón Instalar junto a la extensión de Snyk:

Visual Studio Code muestra la página de la extensión Snyk Security, con detalles del análisis de código y controles de instalación

Después de instalar la extensión, debes autenticarla con tu cuenta de Snyk. Si no tienes una, asegúrate de crear una ahora. Haz clic en el ícono de Snyk e inicia sesión con tus credenciales de Snyk.

A continuación, abre en VS Code el repositorio del proyecto que clonaste. Deberías ver un mensaje que te pregunta si Snyk debe confiar en estas carpetas: haz clic en Confiar en las carpetas y continuar:

El cuadro de advertencia de Snyk indica que el análisis puede ejecutar código y recomienda analizar solo carpetas de confianza, con opciones para confiar en las carpetas o rechazarlas.

Deberías ver algunas líneas onduladas rojas en partes del código con vulnerabilidades de seguridad (es decir, axios.get en la línea 19). Cuando pases el cursor sobre la línea ondulada, aparecerá una ventana emergente con una explicación de la vulnerabilidad.

Código JavaScript en VS Code con una advertencia de seguridad que identifica una vulnerabilidad SSRF en una solicitud axios.get.

También puedes hacer clic en el ícono de Snyk y seleccionar SEGURIDAD DEL CÓDIGO para ver los problemas de seguridad. Al seleccionar una vulnerabilidad, Snyk abre la barra lateral izquierda y proporciona ejemplos de código para ayudarte a resolver el problema.

Panel de vulnerabilidades de Snyk Code que destaca un problema de falsificación de solicitudes del lado del servidor en código Node.js, donde axios solicita una imagen desde una URL ingresada por el usuario

Conclusión

En este artículo aprendiste que es fundamental proteger tus aplicaciones Node.js contra la SSRF, además de conocer algunas estrategias y prácticas recomendadas que pueden ayudarte a lograrlo.

Al usar las versiones más recientes de las bibliotecas, exigir esquemas de URL, implementar listas de permitidos y usar herramientas de seguridad como Snyk, puedes reducir considerablemente la probabilidad de que haya vulnerabilidades SSRF en tus aplicaciones. Además, al integrar Snyk en tu flujo de desarrollo, puedes detectar de manera eficiente la SSRF y otras vulnerabilidades en tu código antes de que lleguen a producción, reforzando tu código y protegiendo tus sistemas contra posibles ataques.

¡No esperes más! Empieza hoy a proteger tus aplicaciones con Snyk.

Empieza con los desafíos de Capture the Flag

Aprende a resolver desafíos de captura la bandera con nuestro taller virtual introductorio a pedido.

Leer más

feature insights context
Blog

Los ataques autónomos ya están aquí. La defensa debe estar a su altura.

Los atacantes autónomos están reduciendo el tiempo disponible para defenderse. Descubre cómo el descubrimiento, la corrección, la validación y la prevención continuos pueden ayudar a los equipos de seguridad a seguirles el ritmo.

illustration hero ai
Blog

El huracán de la IA ha llegado

La IA está acelerando por igual la creación de software y los ciberataques. Los líderes deben proteger los agentes y el código desde el inicio, aplicar controles en tiempo de ejecución y validar las defensas de forma independiente.

feature insights context
Blog

¿La prevención es, en esencia, un problema ya resuelto?

La prevención en el código generado por agentes está resuelta desde el punto de vista arquitectónico, pero elegir controles que protejan la seguridad sin ralentizar el desarrollo sigue siendo el desafío.