Control del riesgo de seguridad Node.js de las dependencias npm

Los paquetes de código abierto, y específicamente npm, son sin duda impresionantes. Hacen a los desarrolladores extremadamente productivos al darnos a cada uno una gran cantidad de funcionalidades existentes que esperan ser consumidas. Si tuviéramos que escribir toda esta funcionalidad nosotros mismos, tendríamos dificultades para crear una fracción de lo que hacemos hoy.

Como resultado, una aplicación típica Node.js hoy en día, consume un montón de paquetes de MNP, a menudo cientos o miles de ellos. Sin embargo, lo que a menudo pasamos por alto es que cada uno de estos paquetes, junto con su funcionalidad, también hace frente a los riesgos de seguridad de Node.js. Muchos paquetes abren nuevos puertos, aumentando así la superficie de ataque. Aproximadamente el 76% de las tiendas de Node usan paquetes vulnerables, algunos de los cuales son extremadamente severos; y los proyectos de código abierto regularmente se vuelven obsoletos, descuidando la reparación de fallas de seguridad.

Inevitablemente, el uso de paquetes npm te expondrá a riesgos de seguridad. Afortunadamente, hay varias preguntas que puede hacer que pueden reducir su riesgo sustancialmente. Esta publicación describe estas preguntas y cómo responderlas.

¿Qué paquetes estoy usando?

Cuantos más paquetes utilices, mayor será el riesgo de tener un paquete vulnerable o malicioso entre ellos. Esto es cierto no solo para los paquetes que usas directamente, sino también para las dependencias indirectas que usan.

Descubrir tus dependencias es tan fácil como ejecutar npm ls en la carpeta principal de tu aplicación, que enumera los paquetes que utilizas. Puedes usar el argumento –prod para mostrar solo las dependencias de producción (que afectan más a tu seguridad) y agregar –long para obtener una breve descripción de cada paquete. Echa un vistazo a esta publicación para comprender mejor cómo puedes dividir y cortar tus depedencias npm.


~/proj/node_redis $ npm ls --prod --long
redis@2.6.0-2
│ /Users/guypod/localproj/playground/node_redis
│ Redis client library
│ git://github.com/NodeRedis/node_redis.git
│ https://github.com/NodeRedis/node_redis
├── double-ended-queue@2.1.0-0
│ Extremely fast double-ended queue implementation
│ git://github.com/petkaantonov/deque.git
│ https://github.com/petkaantonov/deque
├── redis-commands@1.2.0
│ Redis commands
│ git+https://github.com/NodeRedis/redis-commands.git
│ https://github.com/NodeRedis/redis-commonds
└── redis-parser@1.3.0
Javascript Redis protocol (RESP) parser
git+https://github.com/NodeRedis/node-redis-parser.git
https://github.com/NodeRedis/node-redis-parser#readme

Un nuevo grupo de gestión de la dependencia de servicios, tales como bitHound y VersionEye , también pueden listar las dependencias que utilizas, así como realizar un seguimiento de parte de la información a continuación.

Ahora que sabes lo que tienes, puedes hacer algunas preguntas para evaluar el riesgo que implica cada paquete. A continuación hay algunos ejemplos de preguntas que debes hacer, por qué debes hacerlas, y sugerencias sobre cómo puedes obtener respuestas.

¿Sigo usando este paquete?

A medida que pasa el tiempo y tu código cambia, es probable que dejes de usar ciertos paquetes y agregues otros nuevos en su lugar. Sin embargo, los desarrolladores no suelen eliminar un paquete del proyecto cuando dejan de usarlo, ya que alguna otra parte del código puede necesitarlo.

Como resultado, los proyectos tienen una tendencia a acumular dependencias no utilizadas. Si bien no es un problema directamente de seguridad, estas dependencias aumentan innecesariamente la superficie de ataque y añaden desorden al código. Por ejemplo, un atacante puede engañar a un paquete para que cargue un paquete no utilizado con una vulnerabilidad más grave, aumentando el daño potencial.

La verificación de las dependencias no utilizadas se realizan más fácilmente utilizando la herramienta depcheck. depcheck escanea tu código comados requires y import, correlaciona aquellos con los paquetes instalados o mencionados en su package.json, y proporciona un informe. El comando se puede modificar de varias formas mediante los indicadores de comando, lo que facilita la verificación automática de los depósitos no utilizados.


~/proj/Hardy $ depcheck
Unused dependencies
* cucumber
* selenium-standalone
Unused devDependencies
* jasmine-node

¿Están otros desarrolladores usando este paquete?

Los paquetes utilizados por muchos también son observados más de cerca. La probabilidad de que alguien ya haya encontrado y abordado un problema de seguridad en ellos es mayor que en un paquete menos utilizado.

Por ejemplo, el paquete secure-compare se creó para admitir la comparación de cadenas que no fue susceptible a un ataque de tiempo. Sin embargo, una falla fundamental en el paquete llevó a lograr exactamente lo contrario, haciendo ciertas comparaciones extremadamente sensibles al tiempo (e incorrectas).

Si observaras más detenidamente, verías que este paquete se utiliza muy poco, descargado solo 20 veces al día. Si este fuera un paquete más popular, las probabilidades son que alguien hubiera encontrado e informado antes de la falla funcional.

La forma más fácil de evaluar el uso del paquete es su tasa de descarga, indicada en la sección “Estadísticas” de la página del paquete de npm. Puedes extraer esas estadísticas automáticamente usando la API de npm stats, o navegar por las estadísticas históricas en npm-stat.com . Alternativamente, puedes mirar la cantidad de paquetes “Dependientes” – otros paquetes que se usan en la actualidad.

¿Estoy usando la última versión de este paquete?

Los errores, incluidos los de seguridad, se encuentran constantemente y, con suerte, se corrigen. Además, es bastante común ver las vulnerabilidades recién informadas solucionadas solo en la rama principal más reciente de un proyecto.

Por ejemplo, a principios de 2016, se informó sobre una vulnerabilidad de Denegación de Servicio de Expresión Regular (ReDoS) en el paquete HMAChawk.ReDoS es una vulnerabilidad en la que una entrada larga o cuidadosamente elaborada hace que la comparación de expresiones regulares tarde mucho tiempo en calcularse. Mientras tanto, el sub-proceso de procesamiento no atiende nuevas solicitudes, lo que permite un ataque de denegación de servicio con solo un pequeño número de solicitudes.

La vulnerabilidad hawkse solucionó rápidamente en su última versión de la versión principal, 4.x, pero dejó las versiones anteriores sin una solución. Específicamente, dejó una vulnerabilidad no corregida en el paquete request ampliamente utilizado, que usó hawk@3.x. Más tarde, el autor aceptó la solicitud de extracción de Snyk con una solución para la rama 3.x, pero los usuarios request estuvieron expuestos durante un tiempo y el problema aún existe en las sucursales de versiones principales más antiguas. Este es solo un ejemplo, pero como regla general, tus dependencias tienen menos probabilidades de tener errores de seguridad si están en la última versión.

Puedes averiguar si está utilizando o no la última versión con el comando npm outdated. Este comando también admite el indicador –prod para ignorar las dependencias de desarrollo, así como –json para simplificar la automatización. También puedes usar Greenkeeper para informarte de manera proactiva cuando no está usando la última versión.


~/proj/handlebars.js $ npm outdated --prod
Package Current Wanted Latest Location
async 1.5.2 1.5.2 2.0.1 handlebars
source-map 0.4.4 0.4.4 0.5.6 handlebars
uglify-js 2.6.2 2.7.3 2.7.3 handlebars
yargs 3.32.0 3.32.0 5.0.0 handlebars

¿Cuándo se actualizó este paquete por última vez?

Crear un proyecto de código abierto, incluidos los paquetes npm, es divertido. Muchos desarrolladores talentosos crean estos proyectos en su tiempo libre, invirtiendo mucho tiempo y energía en hacerlos buenos. Sin embargo, con el tiempo, la emoción a menudo se desvanece y los cambios en la vida pueden hacer que sea difícil encontrar el tiempo necesario.

Como resultado, los paquetes npm a menudo se vuelven obsoletos, no agregan funciones y corrigen errores lentamente, si es que lo hacen. Esta realidad no es excelente para la funcionalidad, pero es especialmente problemática para la seguridad. Los errores funcionales normalmente solo se interponen en tu camino cuando estás construyendo algo nuevo, lo que permite un margen de maniobra para la rapidez con la que se abordan. Arreglar las vulnerabilidades de seguridad es más urgente: una vez que se conocen, los atacantes pueden explotarlas, por lo que el tiempo para corregirlas es crítico.