Cómo prevenir vulnerabilidades de inyección en registros en aplicaciones JavaScript y Node.js
26 de septiembre de 2024
0 minutos de lecturaEn 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:
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.":

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:

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:


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:

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:
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:

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:

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:

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.



