Skip to main content

Cómo prevenir vulnerabilidades de inyección en registros en aplicaciones JavaScript y Node.js

Escrito por
feature open source

26 de septiembre de 2024

0 minutos de lectura

En muchas aplicaciones empresariales estándar, el registro constante de eventos cumple numerosos propósitos. Ayuda a las empresas a identificar y corregir errores, ofrece información analítica valiosa y permite probar nuevas soluciones. Sin embargo, también hace que las inyecciones en registros sean una de las formas más comunes en que los hackers pueden secuestrar una aplicación o incluso acceder a información confidencial de los usuarios. Las vulnerabilidades en la forma en que las aplicaciones registran datos pueden permitir que los atacantes inyecten código malicioso en los registros y comprometan la integridad y confidencialidad de la aplicación.

En este artículo, nos centraremos en las vulnerabilidades de inyección en registros de Node.js. Explorarás cómo funcionan estas inyecciones y aprenderás formas efectivas de prevenirlas.

¿Qué es una vulnerabilidad de inyección en registros?

Al registrar información, algunas aplicaciones escriben en un archivo externo o dependen de una API externa a la que envían los registros. Una inyección en registros ocurre cuando un atacante manipula los datos de entrada para insertar código malicioso en los registros de la aplicación. Estos registros suelen usarse para depuración, monitoreo y auditoría.

Cuando ocurre una inyección en registros, el atacante puede llenar los registros de datos irrelevantes o incluso ejecutar código en el contexto de la aplicación, lo que genera diversos riesgos de seguridad.

Ejemplo de una aplicación vulnerable de Node.js

Considera una aplicación sencilla de Node.js que usa el framework web Express para registrar datos proporcionados por los usuarios sin validarlos ni depurarlos correctamente. El siguiente fragmento de código muestra esta vulnerabilidad:

const express = require('express');
const app = express();

app.get('/log', (req, res) => {
  const userInput = req.query.data;
  console.log('INFO: ' + userInput);
  res.send('Data logged successfully.');
});

app.listen(3000, () => {
  console.log('Server started on port 3000');
});

Una vez que el servidor esté en funcionamiento de forma local, puedes enviar una solicitud GET con herramientas de prueba de API como Insomnia para comprobar que todo funcione según lo esperado. En este caso, quieres registrar el nombre de usuario de un formulario para ver cómo aparecen los registros del servidor. Agrega los datos que espera el servidor y envía la solicitud. Obtendrás un 200 OK y un texto que dice: "Data logged successfully.":

Cliente de API que muestra una solicitud POST para registrar datos y una respuesta exitosa: “Datos registrados correctamente”

Si revisas los registros del lado del servidor, todo parece registrarse según lo esperado. Como agregaste el valor charlie a los parámetros de datos, el registro muestra INFO: charlie en el servidor:

Terminal que muestra un servidor Node.js Express iniciado en el puerto 3000, con un registro INFO que dice “charlie”

Una forma en que una persona malintencionada podría aprovecharse de cómo esta aplicación registra datos es insertar registros falsos mediante el parámetro data. En este ejemplo, solo tendría que agregar texto adicional a la consulta:

Cliente de API que muestra una solicitud GET a localhost con una carga útil de inyección de registros codificada y una respuesta 200 exitosa.
Terminal que muestra el inicio de un servidor Express de Node.js en el puerto 3000 y el registro de la actividad de eliminación de usuarios

Por ejemplo, una persona malintencionada puede agregar INFO: User deleted como registro falso. Cuando el equipo de desarrollo encuentre este registro, podría confundirse y dedicar tiempo y recursos a averiguar por qué la aplicación elimina usuarios cuando alguien envía un nombre de usuario, todo porque un atacante decidió insertar caracteres adicionales en un formulario de entrada.

Aunque este ejemplo es sencillo, te ayuda a entender qué son las inyecciones en registros en general. Otros tipos de inyección incluyen agregar registros de errores falsos o registros con información falsa de usuarios, simplemente modificando el mensaje del registro o, peor aún, insertando inyecciones de comandos que permitirían a los atacantes manipular directamente los datos internos.

Cómo prevenir vulnerabilidades de inyección en registros en Node.js

Para mitigar las vulnerabilidades de inyección en registros en tus aplicaciones Node.js, considera implementar algunas de estas prácticas comunes:

Depura las entradas de los usuarios

En la mayoría de los casos, las entradas de los usuarios deben validarse y depurarse antes de registrarlas. Esto significa revisar cuidadosamente todo lo que se va a registrar para excluir caracteres que podrían confundirse con otra entrada del registro o incluir únicamente los caracteres que deberían aparecer en los registros.

Siguiendo el ejemplo anterior, una forma de depurar los registros sería incluir una expresión regular que filtre y permita solo letras, lo que impediría agregar caracteres especiales o números al registro:

app.get('/log', (req, res) => {
  const userInput = req.query.data
  const sanitizedInput = userInput.replace(/[^a-zA-Z]+/, '')
  console.log('INFO: Username: ', sanitizedInput);
  res.send('Data logged successfully.');
});
Terminal que muestra el registro de un servidor Node.js Express con una entrada de nombre de usuario y el mensaje «INFO: User deleted»

Un detalle importante que debes recordar es que puede ser complicado elegir la forma correcta de depurar los datos. En este ejemplo, si alguien tiene números en su nombre de usuario (por ejemplo, 123), el nombre de usuario aparecerá modificado en los registros.

Aunque depurar las entradas puede ayudar a detectar registros maliciosos, las concesiones implican que los desarrolladores deben tener mucho cuidado con lo que filtran y con la forma de hacerlo. Por ejemplo, escribir expresiones regulares de manera incorrecta puede provocar vulnerabilidades ReDOS que paralicen las aplicaciones Node.js. Usar paquetes npm de terceros como validator.js es una buena opción para no reinventar la rueda, pero asegúrate de mantener esas dependencias actualizadas.

Ten cuidado con lo que registras

Cada aplicación es única. Un solo estándar de registro no puede contemplar todas las necesidades potenciales de cada aplicación. Es importante considerar qué información necesitas registrar y para quién.

Los registros de nivel DEBUG pueden ayudar a los desarrolladores a rastrear lo que ocurre en la aplicación y cómo el usuario pasa de un componente a otro, o cómo fluye la información de una fuente a otra. Los registros de nivel WARN o ERROR pueden ayudar a los equipos de ingeniería a detectar errores potenciales a tiempo y entender qué provoca que la aplicación se cierre o funcione mal. La clave es registrar solo lo necesario, nada más, y considerar qué ocurriría si los atacantes vulneraran tus registros.

Como explica este artículo de Snyk sobre las vulnerabilidades en el registro de datos, enviar información confidencial a un servicio de registro externo puede hacer que los atacantes vulneren los datos. Por lo general, conviene evitar registrar información confidencial o sensible, como contraseñas, claves de API o datos personales. Si lo haces, asegúrate de implementar controles de acceso adecuados y considera usar variables de entorno (como en GitHub Actions) y sistemas de administración de claves para gestionar de forma segura los datos de configuración confidenciales.

Además de pensar qué registras, debes considerar cómo lo haces. El registro estructurado puede ofrecer una forma más organizada de registrar datos, más fácil de identificar para los desarrolladores. Por ejemplo, en el caso anterior, en lugar de registrar directamente los datos del usuario como una cadena, puedes usar un objeto JSON para etiquetar con mayor claridad lo que se registra:

app.get('/log', (req, res) => {
  const userInput = req.query.data;
  const log = {username: userInput, 'action':'submitted username', 'time': new Date()}
  console.log(log)
  res.send('Data logged successfully.');
});

Más importante aún, estructurar los registros de esta manera puede dificultar que los atacantes los inyecten, ya que también tendrían que manipular muchas propiedades del objeto de registro (es decir, action o time). Si no lo hacen, podrían revelar lo que intentan hacer:

Salida de terminal que muestra un servidor Node.js Express en el puerto 3000 y un nombre registrado que contiene un mensaje de salto de línea inyectado

Aunque este ejemplo de registro de un objeto JSON es sencillo, hay bibliotecas de registro (que veremos a continuación) que ofrecen el registro estructurado como alternativa útil al habitual console.log.

Usa una biblioteca de registro en lugar de console.log

Muchas bibliotecas de registro ofrecen más control y seguridad sobre las salidas de los registros que console.log, ya que suelen incluir capacidades integradas de depuración y filtrado para prevenir ataques de inyección en registros. También permiten a los desarrolladores controlar qué registros se muestran en cada entorno (pruebas o producción), elegir dónde se envían (a un archivo local, una base de datos o una API de terceros) y configurar los registros para que sean más fáciles de rastrear.

Una de las bibliotecas que recomendamos es pino, un paquete bien mantenido y confiable, sin vulnerabilidades de seguridad conocidas.

Pino está altamente optimizado para el rendimiento y se puede configurar para incluir marcas de tiempo en cada entrada del registro. Admite el registro estructurado y los niveles de registro estándar del sistema. Esta complejidad y estructura adicionales pueden mejorar la seguridad y reducir la probabilidad de que los atacantes vulneren los registros.

Usa la extensión de Snyk para VS Code para detectar vulnerabilidades

Snyk ofrece una extensión de IDE gratuita para Visual Studio Code (VS Code) que puede ayudarte a detectar vulnerabilidades de inyección en registros (entre otras) en tu código de Node.js. Así puedes configurarla:

Ve a la pestaña Extensiones en VS Code y busca "Snyk" para encontrar la extensión "Snyk Security - Code, Open Source Dependencies, Iac Configurations". Haz clic en Install:

Marketplace de extensiones de Visual Studio Code que muestra la extensión Snyk Security con un botón para instalar

Una vez instalada, Snyk te pedirá que autentiques tu cuenta conectándola a tu IDE. Haz clic en Connect VS Code with Snyk. Se abrirá una página web externa de Snyk que te pedirá permiso para conectarte. Una vez allí, haz clic en Authenticate.

Una vez que te hayas autenticado, haz clic en Scan para analizar tu código en busca de vulnerabilidades. También puedes configurar la extensión según lo necesites:

Panel de seguridad de Snyk Code en un editor de código que muestra seis vulnerabilidades y 55 problemas de calidad del código en archivos JavaScript

La extensión de Snyk para IDE analiza automáticamente tu código para detectar vulnerabilidades de inyección en registros. Además, durante el análisis, Snyk destaca otros posibles problemas de seguridad en tu código, como la presencia de secretos codificados de forma fija o vulnerabilidades de recorrido de rutas, y ofrece información práctica y sugerencias para ayudarte a proteger tu aplicación.

Empieza a usar Snyk gratis

No necesitas tarjeta de crédito.

Crea una cuenta con Bitbucket y más opciones

Al usar Snyk, aceptas cumplir nuestras políticas, incluidos nuestros Términos del servicio y nuestra Política de privacidad.

Leer más

illustration hero ai
Blog

PHP corrige dos vulnerabilidades críticas de RCE sin autenticación (CVE-2026-109618, CVE-2026-109619): actualiza ahora

Dos fallas críticas en el ciclo de solicitudes de PHP pueden permitir la ejecución remota de código sin autenticación. Actualiza a una versión corregida o instala una corrección retroportada por un proveedor para las versiones anteriores.

blog feature ai lilac
Blog

Convierte tu backlog de vulnerabilidades en pull requests con la remediación agéntica

Evo Remediation Agent ya está disponible en Open Preview e incorpora la resolución automatizada de dependencias a Evo Agentic AppSec. Ahora, los equipos de AppSec pueden ejecutar campañas de remediación continuas

AI Background min crop
Blog

La IA activa todos los riesgos que has aceptado: siete ideas del panel de seguridad de IA sin filtros

La IA hace que los ataques sean más baratos y rápidos, y deja al descubierto riesgos que las organizaciones ya enfrentan. Cuatro líderes de seguridad explican por qué la defensa a velocidad de máquina y la corrección más rápida son esenciales.