Cómo prevenir el control de acceso deficiente en aplicaciones Node.js con Express
Ben Smitthimedhin
22 de mayo de 2024
0 minutos de lecturaEl control de acceso en las aplicaciones backend de Node.js es fundamental para las aplicaciones web creadas con el framework Express. Garantiza que los usuarios solo puedan acceder a los datos y las funciones que tienen autorización para usar. Sin embargo, cuando el control de acceso se ve comprometido, los usuarios pueden acceder a datos que no deberían estar disponibles para ellos. Esto es especialmente problemático si los atacantes intentan manipular o robar datos privados.
Es fundamental que comprendas y prevengas las vulnerabilidades de control de acceso, ya que la filtración de datos privados puede tener consecuencias desastrosas para muchas empresas. En este artículo, aprenderás más sobre el control de acceso deficiente en aplicaciones Node.js y las estrategias para prevenir estas vulnerabilidades al crear aplicaciones web con el framework Express.
¿Qué es el control de acceso deficiente?
El control de acceso consiste en limitar lo que un usuario puede hacer y ver dentro de una aplicación. El control de acceso es deficiente cuando hay fallas en la implementación de estas reglas, lo que permite que usuarios no autorizados accedan a información confidencial o realicen acciones restringidas.
Por ejemplo, si debido a un control de acceso defectuoso, un usuario común puede ver los datos privados de otro usuario, cambiar su contraseña o incluso elevar sus privilegios al nivel de administrador, las consecuencias pueden ser catastróficas: desde filtraciones de datos hasta modificaciones no autorizadas del sistema.
En la siguiente sección, verás algunos ejemplos de código que muestran distintos tipos de vulnerabilidades de control de acceso.
Control de acceso vertical deficiente en el middleware de Express
Un tipo de vulnerabilidad de control de acceso se produce cuando hay una falla o debilidad en el control de acceso vertical. Esto ocurre cuando un usuario común obtiene acceso a funciones de nivel administrativo u otros privilegios que no debería tener. Por lo tanto, este usuario común asciende verticalmente en cuanto a privilegios y puede realizar modificaciones, eliminaciones u otras funciones propias de un administrador que no debería poder ejecutar.
A continuación, se presentan algunas vulnerabilidades de código específicas que pueden provocar un control de acceso vertical deficiente:
Panel de administración desprotegido en las rutas de Express
Un panel de administración está desprotegido cuando las rutas que conducen a funciones de nivel administrativo en una página web o un servidor no cuentan con ningún tipo de autorización:
En este escenario, quien realice una solicitud GET a la ruta /admin obtiene automáticamente acceso al mensaje Welcome to the Admin Panel!, tenga o no una sesión iniciada o los permisos necesarios. El panel /admin no está vinculado a la ruta /login, como debería, por lo que se trata de una forma de control de acceso deficiente.
Panel de administración de Express basado en parámetros de consulta
Es sencillo implementar un panel de administración basado en parámetros de URL; sin embargo, sin algún tipo de validación, puede ser bastante fácil manipularlo:
En este ejemplo, un usuario que realiza una solicitud GET directamente a la ruta /admin recibe la respuesta Access denied. Para evadir esta denegación, solo hace falta agregar ?isAdmin=true a la URL. Como cualquier persona puede agregar estos parámetros, sin importar quién acceda al panel de administración, se repite la misma vulnerabilidad del primer ejemplo: un usuario común obtiene privilegios de administrador.
Control de acceso en las rutas de Express mediante la oscuridad
En teoría, se podría ocultar la ruta del panel de administración haciendo que sea difícil de adivinar, como en el siguiente middleware de ruta de Express:
Esto parece hacer más seguro el panel de administración, en cierto sentido, pero es una forma insegura e incorrecta de proteger una aplicación. Los atacantes pueden encontrar la manera de acceder al mapa del sitio y exponer todos los endpoints que contiene un sitio web. También pueden usar técnicas de fuzzing para probar todos los endpoints posibles y ver qué tipo de respuesta reciben. Incluso podrían intentar acceder a los archivos de registro para encontrar la URL registrada que permite entrar al panel de administración. Aunque este método intenta ser ingenioso, en realidad no crea ni proporciona una seguridad sensata y, en última instancia, perpetúa una vulnerabilidad de control de acceso deficiente.
Registro en texto sin formato en aplicaciones Express
Los desarrolladores también deben tener cuidado con los mecanismos de registro para evitar que se divulgue información crítica a los usuarios de la aplicación por accidente. Por ejemplo, una aplicación podría crear una entrada de registro para una acción de inicio de sesión que incluya información confidencial, sin eliminar la contraseña de los datos del registro:
Observa la instrucción console.log al final. Cualquiera que tenga acceso a los archivos de registro también tendría acceso a los datos de los usuarios, lo que le permitiría iniciar sesión como cualquier persona con acceso administrativo. Por eso es importante registrar solo la información necesaria en cada contexto. Además, por lo general conviene asegurarse de que nadie registre información crítica que permita a los hackers explotar los datos en caso de una brecha.
Control de acceso horizontal deficiente en el middleware de Express
Mientras que los problemas de control de acceso vertical incluyen situaciones que permitirían a los usuarios elevar sus privilegios, los problemas de control de acceso horizontal ocurren cuando un usuario obtiene acceso a recursos o datos que pertenecen a otros usuarios del mismo nivel.
Veamos algunos ejemplos específicos de código con vulnerabilidades de control de acceso horizontal en el framework web Express para Node.js.
ID de usuario en un parámetro de solicitud enviado a una ruta de Express
Una aplicación que permite a los usuarios ver su ID de usuario en el parámetro de solicitud puede ser problemática, especialmente si se puede acceder a los datos de un usuario usando ese mismo ID en los parámetros de solicitud. Esto significa que basta con conocer los ID de otras personas para acceder a sus datos.
Por ejemplo, si tu ID es 123, para acceder a la información de tu cuenta en un sitio web tendrías que ir a www.[website name].com/account?userId=123:
En este ejemplo, al realizar una solicitud GET a /account con la consulta userId=1, puedes obtener fácilmente el objeto accountData de logan@test.com, aunque hayas iniciado sesión como otra persona. Si realizas la misma solicitud GET con la consulta userId=2, obtienes el objeto accountData de megan@test.com. Basta con usar en el parámetro el ID asociado a un usuario para que los resultados de la consulta devuelvan el objeto accountData
Esta vulnerabilidad de seguridad se conoce comúnmente como referencias directas inseguras a objetos (IDOR) y se encuentra en Java, Ruby y otros lenguajes de programación.
ID de usuario impredecible en un parámetro de solicitud que llega al middleware de una ruta de Express
A partir del ejemplo anterior, podrías pensar que un control de acceso bien diseñado consiste simplemente en ocultar el ID de usuario. Por ejemplo, mediante algunas modificaciones, como aplicar un hash y codificar el ID 1 de un usuario, este podría convertirse en 8a76dh3t en la consulta, lo que dificultaría el acceso a los datos de otras personas:
En este ejemplo, una función obfuscateUserId toma el ID de usuario de la lista de usuarios existentes y lo oculta. Por lo tanto, para realizar una solicitud GET a la ruta /account y obtener información de la cuenta, es necesario conocer y colocar el ID oculto en el parámetro de solicitud (por ejemplo, userId=8a76dh3t).
Este método no se basa en controles de seguridad prácticos y solo perpetúa el problema del control de acceso deficiente. Con suficiente tiempo y dedicación, los hackers aún pueden probar distintos valores mediante fuzzing y adivinar el ID de usuario oculto correcto para entrar, lo que vuelve a dejarte con un control de acceso deficiente.
Aprovechar la falta de protección contra CSRF en aplicaciones Express como vector de ataque
Por último, una forma común en que los actores maliciosos podrían intentar explotar el control de acceso deficiente de tu aplicación Express es mediante ataques de falsificación de solicitudes entre sitios (CSRF). En pocas palabras, estos ataques crean páginas web o enlaces falsos que te engañan para que envíes tu información. Luego, esta información se adjunta a una función que envía una solicitud legítima al sitio web real, donde tus datos quedan accesibles.
Si no se implementa la protección contra CSRF, todas las aplicaciones de servidor de ejemplo que revisaste pueden incorporarse en un formulario falso en una página web. Allí, el usuario ingresa datos y los envía para que una parte maliciosa los explote.
Cómo prevenir las vulnerabilidades de control de acceso deficiente en Node.js
Ahora que viste algunos ejemplos de vulnerabilidades de control de acceso vertical y horizontal, es momento de revisar estrategias generales para prevenirlas.
Usa las versiones más recientes de las bibliotecas
Una forma de garantizar que tu control de acceso cumpla con estándares más altos es usar paquetes npm confiables, mantenidos y protegidos. Sin embargo, Node.js y su ecosistema de paquetes reciben actualizaciones frecuentes para corregir vulnerabilidades de seguridad, por lo que también es fundamental mantenerlos actualizados.
Mantener actualizado el entorno de ejecución de Node.js y sus paquetes es fundamental para reducir el riesgo de problemas de seguridad conocidos. Una forma de hacerlo es comprobar el estado de tu archivo package.json con Snyk Advisor, que te indica qué bibliotecas contienen vulnerabilidades que debes corregir y si un paquete npm está obsoleto o ya no recibe mantenimiento.
Si arrastras y sueltas un archivo package.json de ejemplo en Snyk Advisor, verás que este proyecto contiene varios paquetes npm potencialmente problemáticos:

Al analizarlo más de cerca, Snyk Advisor te muestra que loader no ha tenido versiones nuevas en ocho años y no es muy popular entre los usuarios de npm en comparación con otros paquetes. Revisar las actualizaciones con regularidad y aplicarlas de inmediato, o incluso reemplazar las bibliotecas obsoletas de tu proyecto, son buenos primeros pasos para mantener seguro el control de acceso.
No dependas únicamente de la ofuscación
Como viste en algunos de los ejemplos de aplicaciones, la seguridad basada en la oscuridad no es una estrategia confiable. Si un atacante puede adivinar cómo obtener acceso (o probar distintas opciones mediante fuzzing), tu aplicación no es segura. Debes implementar mecanismos adecuados de control de acceso y comprobaciones de autenticación para garantizar que solo los usuarios autorizados accedan a funciones y datos confidenciales.
En el ejemplo anterior, tenías una ruta con números difíciles de adivinar como endpoint:
En la práctica, suele ser más seguro confiar en el método probado de proteger los datos con esquemas de autenticación adecuados antes de conceder el acceso. Se recomienda usar bibliotecas de inicio de sesión más seguras, como Passport.js o AWS Cognito, pero aquí se muestra un esquema básico de una página de inicio de sesión sin bibliotecas externas con fines educativos:
En lugar de ocultar la ruta del panel de administración, los usuarios pueden acceder directamente a /login, pero deben proporcionar el correo electrónico y la contraseña correctos en el cuerpo de la solicitud para obtener acceso al panel de administración. Así, no dependes de métodos de ofuscación riesgosos y puedes controlar mejor la información o las respuestas que recibe cada usuario.
Deniega el acceso de forma predeterminada y otorga permisos granulares
Al decidir qué usuarios deben acceder a los distintos tipos de datos de tu aplicación, suele ser conveniente empezar desde cero. Debes adoptar el principio de privilegio mínimo, según el cual se deniega inicialmente a los usuarios el acceso a todo y solo se les permite acceder a recursos y realizar acciones específicas cuando sea necesario.
Además, debes implementar un control de acceso detallado que avance de forma gradual y garantice que los usuarios solo puedan realizar las acciones para las que tienen autorización explícita. En el ejemplo anterior, agregar roles adicionales puede ayudarte a establecer controles más específicos:
Aquí, los privilegios más altos están reservados para el “Level 3 panel”, al que solo pueden acceder los usuarios con el rol de admin. Los demás usuarios que inicien sesión con el rol de manager tendrán acceso a un “Level 2 panel”, que, por ejemplo, podría mostrar información sobre cada usuario, pero no incluiría un botón para eliminar.
Cualquier persona a quien no se le asigne explícitamente un rol recibirá automáticamente la respuesta “Unauthorized access”. Otorgar permisos específicos a los usuarios y ampliarlos gradualmente cuando sea necesario te permite evitar dar privilegios adicionales a quienes no deberían tenerlos.
Realiza auditorías y pruebas exhaustivas
Audita tu base de código con regularidad y realiza pruebas de seguridad, como análisis estático de código, análisis de dependencias y revisiones de código seguro, para identificar vulnerabilidades de control de acceso y otros problemas de seguridad. Puede ser útil guardar en favoritos esta lista (u otra) de posibles vulnerabilidades de control de acceso y usarla para comprobar que tu aplicación no las tenga. Además, agregar pruebas unitarias en toda la aplicación es una práctica estándar para garantizar que cada línea de código funcione como esperas.
Siempre conviene considerar herramientas de análisis automatizado, como las de pruebas de seguridad de aplicaciones estáticas (SAST), para identificar problemas de seguridad comunes. Con la extensión de Snyk para VS Code, recibes comentarios rápidos de los análisis de seguridad cada vez que guardas tus archivos JavaScript, junto con recomendaciones automatizadas para corregir los problemas.
Usa la extensión de Snyk para IDE en Visual Studio Code
Una herramienta poderosa que vale la pena destacar es Snyk. Aunque Snyk ofrece distintas herramientas para proteger tu aplicación, su extensión para Visual Studio (VS) Code puede ayudarte a detectar y corregir vulnerabilidades de control de acceso defectuoso en tu código Node.js mientras lo escribes.
Así puedes configurar la extensión de Snyk y aprovecharla al máximo:
Ve a la pestaña Extensiones de VS Code y busca "Snyk" para encontrar la extensión Snyk Security - Code, Open Source Dependencies, IaC Configurations. Haz clic en Instalar:

Una vez instalada, Snyk te pedirá que autentiques tu cuenta conectándola con tu IDE. Haz clic en Connect VS Code with Snyk. Se abrirá una página web externa de Snyk que te pedirá permiso para conectarse. Allí, haz clic en Authenticate.
Una vez que hayas iniciado sesión, haz clic en Scan para analizar tu código en busca de vulnerabilidades. También puedes configurar la extensión según lo necesites.
Con los ejemplos anteriores de las rutas /admin, /login y /account, Snyk detectó doce vulnerabilidades de seguridad. Además, Snyk analiza tu código para encontrar posibles problemas de calidad, configuración y seguridad de código abierto, tanto en tu aplicación como en las bibliotecas que utiliza:


La extensión de Snyk analiza tu código para detectar vulnerabilidades de seguridad conocidas, incluidas las relacionadas con el control de acceso. Ofrece comentarios en tiempo real y sugiere soluciones para los problemas que identifica. Con esta extensión, puedes detectar y corregir vulnerabilidades de control de acceso defectuoso antes de que lleguen a tu entorno de producción.
Además, Snyk ofrece distintas herramientas de análisis de código que puedes usar. Al igual que la extensión para VS Code, estas herramientas están diseñadas para encontrar problemas de seguridad en tu código mientras lo escribes, por lo que son esenciales para identificar y resolver problemas de control de acceso en tiempo real.
Conclusión
En este artículo, aprendiste sobre el control de acceso defectuoso en Node.js, un problema de seguridad crítico que puede permitir el acceso no autorizado a datos y privilegios en aplicaciones creadas con el framework web Express. Desde rutas de Express a paneles de administración sin protección hasta identificadores de usuario impredecibles en los parámetros de las solicitudes, los ejemplos del artículo ilustran los riesgos potenciales del control de acceso defectuoso.
Para evitar vulnerabilidades de control de acceso defectuoso en tus aplicaciones Node.js, puedes implementar varias estrategias clave, como usar las versiones más recientes de las bibliotecas, evitar métodos basados en la seguridad por oscuridad, aplicar el principio de privilegio mínimo, realizar auditorías y pruebas exhaustivas, y aprovechar herramientas como la extensión de Snyk para Visual Studio Code. En conjunto, estos métodos pueden mejorar significativamente la seguridad de tu aplicación. Recuerda que crear y mantener una aplicación segura es un proceso continuo. Mantenerte alerta es clave para preservar la integridad de tus aplicaciones y datos.
¡Usa Snyk para empezar a corregir tus problemas de seguridad hoy mismo!


