[marcador] 23 recomendaciones para proteger aplicaciones Node.js

En estos días, los servicios web están constantemente expuestos a una variedad de ataques. Por lo tanto, la seguridad es algo para recordar en todas las etapas del ciclo de vida del proyecto. Los autores del material, cuya traducción publicamos hoy, mantienen un repositorio en GitHub, que contiene alrededor de 80 recomendaciones para proteger las aplicaciones que se ejecutan en la plataforma Node.js. Este material, basado en muchas publicaciones de seguridad, tiene más de dos docenas de recomendaciones sobre Node.js y algunos consejos generales. Además, este material cubre las 10 vulnerabilidades principales de la lista del proyecto OWASP.



1. Use las reglas para el linter, destinadas a verificar la seguridad del código



Recomendaciones


Durante el desarrollo, use un complemento orientado a la seguridad para el linter, como eslint-plugin-security . Esto le permite identificar vulnerabilidades y problemas de seguridad muy pronto, al momento de escribir el código apropiado. Este enfoque ayuda a encontrar debilidades en la seguridad del programa. Entre ellos están el uso del comando eval , la invocación de procesos secundarios, la importación de módulos con la transferencia al comando correspondiente de algo diferente de un literal de cadena (digamos, una cierta cadena formada en base a los datos transmitidos al servidor por el usuario).

Aquí hay material útil sobre las reglas de linter

Adam Baldwin dice lo siguiente sobre linting: “Linter no debe ser solo una herramienta que siga meticulosamente las reglas con respecto al número de espacios utilizados, punto y coma, y ​​el uso del comando eval. ESLint proporciona al desarrollador una plataforma poderosa que puede detectar y eliminar una amplia gama de patrones potencialmente peligrosos en el código. Estamos hablando, por ejemplo, de expresiones regulares, sobre la comprobación de la entrada del usuario, etc. Creo que linter ofrece a los desarrolladores que están preocupados por los problemas de seguridad una herramienta nueva y poderosa a la que vale la pena prestarle atención ".

Posibles problemas


Lo que parece una falla de seguridad menor durante el desarrollo se está convirtiendo en una grave vulnerabilidad en la producción. Además, si todos los desarrolladores de proyectos no siguen reglas de seguridad uniformes cuando trabajan con código, esto puede generar vulnerabilidades en él o, por ejemplo, la penetración de datos confidenciales en repositorios públicos.

2. Limite el número de solicitudes simultáneas del servidor



Recomendaciones


Los ataques DoS son muy populares entre los ciberdelincuentes y son relativamente fáciles de llevar a cabo. Puede implementar un sistema para limitar la cantidad de solicitudes a una aplicación que utiliza un servicio externo, como un equilibrador de carga en la nube, un firewall en la nube, un servidor nginx o, para aplicaciones pequeñas que no son críticas, usar middleware para limitar la cantidad de solicitudes como express-rate -limit .

Aquí está el material sobre la implementación del sistema para limitar la frecuencia de las solicitudes al servidor

Posibles problemas


Una aplicación que no proporciona un sistema para limitar el número de solicitudes simultáneas puede ser atacada, lo que conducirá a su falla. Esto se expresa en el hecho de que los usuarios de dicha aplicación tendrán dificultades para trabajar con ella o no podrán interactuar con ella en absoluto.

3. Eliminar información confidencial de los archivos de configuración o cifrarlos



Recomendaciones


Nunca almacene datos confidenciales en texto plano en archivos de configuración o en código. En su lugar, use sistemas de gestión de datos confidenciales, como los productos Vault o los sistemas Kubernetes / Docker Secrets, o use variables de entorno para almacenar dichos datos. Los datos confidenciales almacenados en el sistema de control de versiones deben estar encriptados, se deben tomar medidas para su almacenamiento y uso seguro. Entre tales medidas están el uso de claves dinámicas, el uso de fechas de vencimiento de contraseñas, auditorías de seguridad, etc. Utilice los sistemas para verificar el código antes de confirmarlo o antes de enviarlo al repositorio para evitar el envío accidental de datos confidenciales al repositorio público.

Aquí hay material sobre la gestión de datos confidenciales

Posibles problemas


Incluso si el código se almacena en un repositorio cerrado, un día, por error, puede estar disponible públicamente. En este punto, todos los datos confidenciales almacenados en él pasarán a ser de dominio público. Como resultado, el acceso de terceros al repositorio con el código inadvertidamente los llevará a obtener acceso a los sistemas asociados con él (bases de datos, API, servicios, etc.).

4. Prevenir vulnerabilidades de inyección de código



Recomendaciones


Para evitar inyecciones SQL / NoSQL y otros ataques similares, use siempre bibliotecas ORM / ODM o mecanismos DBMS destinados a la limpieza de datos o al soporte de consultas parametrizadas con nombre o indexadas, y verifique lo que viene del usuario. Nunca use, para incrustar ciertos valores en textos de consulta, solo cadenas de plantilla JavaScript o concatenación de cadenas, ya que este enfoque abre su aplicación a una amplia gama de vulnerabilidades. Todas las bibliotecas respetables para Node.js utilizadas para trabajar con datos (por ejemplo, Sequelize , Knex , mangosta ) contienen protección incorporada contra ataques mediante la inyección de código.

Aquí está el material sobre prevención de inyección usando bibliotecas ORM / ODM

Posibles problemas


El uso de datos no verificados y sin limpiar recibidos del usuario en consultas puede conducir a un ataque al introducir un operador cuando se trabaja con una base de datos NoSQL como MongoDB. Si no utiliza un sistema de limpieza de datos o una biblioteca ORM cuando trabaja con una base de datos SQL, esto dará lugar a la posibilidad de un ataque por inyección SQL, lo que crea un gran agujero de seguridad en la aplicación.

5. Evite los ataques DoS estableciendo explícitamente las condiciones para la finalización anormal del proceso.


Recomendaciones


El proceso Nodo se bloquea cuando se produce un error no controlado. Pero en muchas recomendaciones que reflejan las mejores prácticas para Node, se recomienda que los procesos se terminen incluso cuando se haya interceptado y procesado un error. Express, por ejemplo, se bloqueará cuando ocurra cualquier error asincrónico, a menos que las rutas estén envueltas en expresiones catch . Este hecho ofrece a los atacantes una oportunidad muy atractiva. Ellos, habiendo descubierto que cuando llega una determinada solicitud, el proceso se bloquea, comienzan a enviarle esas solicitudes. No hay ninguna recomendación que resuelva este problema de una sola vez, sin embargo, algunos trucos pueden mitigarlo. Entonces, al final del proceso debido a un error no manejado, debe notificar al administrador, dándole a dicha notificación la más alta prioridad de importancia. Es necesario verificar lo que llega al proceso en las solicitudes y evitar situaciones de terminación anormal del proceso debido a solicitudes que, accidental o intencionalmente, se forman incorrectamente. Todas las rutas deben estar envueltas en expresiones catch y el sistema debe estar configurado para que, si la solicitud fue la causa del error, el proceso no se bloquee (a diferencia de lo que sucede a nivel de aplicación global).

Posibles problemas


Analicemos la siguiente situación. Hay muchas aplicaciones de Node.js. ¿Qué sucede si comenzamos a enviarles solicitudes POST con JSON vacío como cuerpo de la solicitud? Esto hará que muchas de estas aplicaciones se bloqueen.

Ahora, si tuviéramos que desempeñar el papel de ciberdelincuentes, para evitar que las aplicaciones se bloqueen, sería suficiente continuar enviándoles solicitudes similares.

6. Configure los encabezados de respuesta HTTP para aumentar la seguridad del proyecto



Recomendaciones


Una aplicación debe usar encabezados HTTP orientados a la seguridad para evitar que los atacantes recurran a técnicas de ataque comunes como scripting entre sitios (XSS), clickjacking y otros. Personalizar los encabezados es fácil con módulos especiales como el casco .

Aquí hay material sobre el uso de encabezados seguros

Posibles problemas


Si no se utilizan encabezados HTTP seguros, los atacantes podrán llevar a cabo ataques contra los usuarios de sus aplicaciones, lo que genera enormes vulnerabilidades.

7. Continuamente, automáticamente, verifique sus proyectos para el uso de dependencias vulnerables en ellos



Recomendaciones


En el ecosistema NPM, los proyectos con muchas dependencias son muy comunes. Las dependencias siempre deben controlarse, dado el descubrimiento de nuevas vulnerabilidades. Use herramientas como npm audit , nsp o snyk para detectar, monitorear y corregir dependencias vulnerables. Incruste estas herramientas en su sistema de integración continua. Esto le permitirá detectar dependencias vulnerables antes de que entren en producción.

Aquí hay material sobre seguridad de dependencia del proyecto

Posibles problemas


Un atacante puede determinar el marco web utilizado en el proyecto y realizar ataques a todas las vulnerabilidades conocidas.

8. Intente no usar el módulo criptográfico estándar Node.js para el procesamiento de contraseñas, use Bcrypt en su lugar



Recomendaciones


Las contraseñas u otros datos confidenciales (claves API, por ejemplo) deben almacenarse procesándolos con funciones criptográficas utilizando "salt", como Bcrypt. Vale la pena usar exactamente algo similar, y no el módulo crypto estándar Node.js por razones de seguridad y rendimiento.

Aquí está el material sobre Bcrypt

Posibles problemas


Las contraseñas o algunos datos confidenciales que se almacenan sin la aplicación de medidas apropiadas para protegerlos son vulnerables a los ataques de fuerza bruta y ataques de diccionario, que, como resultado, conducen a la divulgación de dichos datos.

9. Utilice sistemas de escape de caracteres en datos HTML, JS y CSS enviados al usuario



Recomendaciones


Si algunos datos se envían al navegador del usuario desde una fuente no confiable, entonces, incluso si simplemente se deben mostrar, dichos datos pueden ser un código que se puede ejecutar. Esto se conoce comúnmente como scripting entre sitios (XSS). Puede reducir el riesgo de que tales ataques sean posibles utilizando bibliotecas especiales que procesan datos para que no puedan ejecutarse. Esto se llama codificación o protección de datos.

Aquí hay material sobre cómo proteger la salida

Posibles problemas


Si no le importa el blindaje de datos, un atacante puede, por ejemplo, guardar código JavaScript malicioso en su base de datos, que puede transmitirse a los clientes sin cambios y lanzarse.

10. Verifique los datos JSON que llegan al servidor



Recomendaciones


Controle el contenido de los cuerpos de las solicitudes entrantes, verificando si corresponden a lo que espera ver en dichas solicitudes. Si la solicitud no se ve como se esperaba, deje de procesarla rápidamente. Para evitar la operación de escribir el código de verificación de solicitud para cada ruta, puede usar herramientas JSON livianas para la validación de datos, como jsonschema o joi .

Aquí está el material para verificar los datos JSON entrantes

Posibles problemas


Si el servidor acepta cordialmente cualquier solicitud sin examinarla minuciosamente, esto aumenta enormemente la superficie de ataque de la aplicación e inspira a los atacantes a tratar de encontrar muchas de ellas que conducen a un "bloqueo" del sistema.

11. Mantener tokens JWT en la lista negra



Recomendaciones


Cuando se usan tokens JWT (por ejemplo, si trabaja con Passport.js ), de manera predeterminada, no existe un mecanismo estándar para revocar los privilegios de acceso al sistema para tokens ya emitidos. Incluso si encuentra que cierto usuario está haciendo algo obviamente anormal, no tiene forma, a través del mecanismo de token, de bloquear su acceso al sistema, siempre que tenga un token válido. Este problema puede mitigarse implementando una lista negra de tokens no confiables, cuya validación se realiza en cada solicitud.

Aquí está el material sobre los tokens JWT en la lista negra

Posibles problemas


Un atacante puede usar las fichas que caen en las manos equivocadas. Podrá acceder a la aplicación y hacerse pasar por un usuario común: el propietario del token.

12. Limite el número de intentos de inicio de sesión



Recomendaciones


En aplicaciones basadas en express, para protegerse contra ataques de fuerza bruta y ataques de diccionario, vale la pena usar el middleware apropiado, como express-brute . Del mismo modo, debe proteger rutas críticas como /admin o /login . La protección debe basarse en el análisis de las propiedades de la consulta, como el nombre de usuario utilizado en la consulta u otros identificadores, como los parámetros del cuerpo de la consulta.

Aquí está el material para limitar el número de intentos de inicio de sesión

Posibles problemas


Si la aplicación no limita el número de intentos de inicio de sesión, el atacante puede enviar automáticamente un número ilimitado de solicitudes de inicio de sesión a su sistema, por ejemplo, intentando obtener acceso a una cuenta privilegiada.

13. Ejecute Node.js como usuario no root



Recomendaciones


Un escenario extremadamente común es cuando Node.js se ejecuta como usuario root con privilegios ilimitados. Por ejemplo, así es como todo está configurado de forma predeterminada en los contenedores Docker. Se recomienda crear un usuario que no tenga privilegios de root, e incrustarlo en la imagen de Docker, o iniciar el proceso en nombre de ese usuario llamando al contenedor con el indicador de -u username .

Aquí está el material sobre el lanzamiento de Node.js como usuario no root

Posibles problemas


Si Node.js se ejecuta bajo la cuenta de usuario root, entonces un atacante que pudo ejecutar un script en el servidor tiene posibilidades ilimitadas en la máquina local. Digamos que puede cambiar la configuración de iptable y redirigir el tráfico a su propia computadora.

14. Limite la cantidad de datos transmitidos en las solicitudes utilizando un servidor proxy inverso o middleware



Recomendaciones


Cuanto mayor sea la cantidad de datos en el cuerpo de la solicitud, más difícil será para un servidor de un solo subproceso procesar dicha solicitud. El uso de solicitudes grandes le da al atacante la oportunidad de inundar el servidor con trabajo innecesario sin enviarle una gran cantidad de solicitudes (es decir, sin realizar un ataque DoS / DDoS). Puede reducir el riesgo de tales ataques limitando el tamaño de los cuerpos de las solicitudes entrantes en un determinado sistema de borde (en un firewall o en un equilibrador de carga), o configurando body-parser express para recibir solo paquetes que contengan una pequeña cantidad de datos.

Aquí hay material sobre la limitación de la cantidad de datos transmitidos en las solicitudes

Posibles problemas


Si no limita la cantidad de datos transmitidos en las solicitudes, un atacante puede cargar la aplicación procesando solicitudes grandes. En este momento, no podrá resolver las tareas para las que está diseñado. Esto conduce a un bajo rendimiento y hace que la aplicación sea vulnerable a los ataques DoS.

15. Evite usar la función eval en JavaScript



Recomendaciones


La función eval es incorrecta porque le permite ejecutar código JS arbitrario que se le pasa durante la ejecución del programa. Además, está lejos de ser solo que este código puede ralentizar la aplicación. Esta función plantea un grave riesgo de seguridad, ya que el código JS malicioso enviado al servidor por un atacante podría entrar en él.

Además, debe evitar el new Function constructor de new Function . Las funciones setTimeout y setInterval nunca necesitan pasar código JS generado dinámicamente.

Aquí está el material sobre eval

Posibles problemas


Si alguien encuentra una manera de transferir código JS malicioso, en forma de texto, una función de eval o algún otro motor JS similar, tendrá acceso completo a la página, a todo lo que se puede hacer usando JavaScript. Esta vulnerabilidad a menudo se asocia con ataques XSS.

16. No sobrecargue las aplicaciones Node.js de subproceso único con expresiones regulares maliciosas



Recomendaciones


Las expresiones regulares, aunque convenientes, representan una amenaza para las aplicaciones JavaScript en general, y en particular para la plataforma Node.js. Procesar con expresión regular lo que vino del usuario puede crear una gran carga en el procesador. Por ejemplo, procesar expresiones regulares puede ser tan ineficiente que verificar diez palabras puede bloquear el ciclo de eventos durante varios segundos y sobrecargar el procesador. Por lo tanto, es mejor usar paquetes de terceros para verificar datos de cadena, como validator.js , en lugar de escribir sus propias expresiones regulares. Puede usar el paquete safe-regex para detectar patrones de expresiones regulares vulnerables.

Aquí está el material anti-malware regex

Posibles problemas


Las expresiones regulares mal escritas pueden estar sujetas a un tipo especial de ataques DoS, durante el cual el bucle de eventos está completamente bloqueado. Por ejemplo, en noviembre de 2017, se descubrió que el paquete de moment popular era vulnerable a tales ataques.

17. Evite cargar módulos usando variables



Recomendaciones


Evite importar archivos cuya ruta se especifique como parámetro, en función de las consideraciones según las cuales este parámetro se puede establecer en función de los datos del usuario. Esta regla se puede ampliar para incluir aquí y, en general, el acceso a los archivos (usando fs.readFile() ) y el acceso a cualquier otro recurso importante usando los parámetros recibidos del usuario. El uso de eslint-plugin-security hace posible detectar estos patrones inseguros muy temprano.

Aquí hay material sobre la carga segura del módulo

Posibles problemas


Los datos enviados al servidor por un atacante pueden estar en la configuración que es responsable de importar ciertos archivos, entre los cuales puede haber un archivo malicioso que se cargó previamente en el servidor. Estos datos también se pueden usar para acceder a los archivos del sistema que ya están en la computadora.

18.




, (, ), «», . , ( cluster.fork() ), npm-, .




, , . — , , , .

19.




, , . , , , . child_process.execFile , , , , , .

,


, , , , .

20.




express, , . , , , Error ( ). , , , - , .




, , , , , , .

21. npm Yarn




, -, (MFA, multi-factor authentication). npm Yarn , . , , , . , . , , npm, .


, ? eslint, .

22. ,




- . , — , - . , , X-Powered-By , , . , ( , , Node.js express).

-


- . , , -, — . .

23.


, . — , Node.js. , OWASP .

  • root- .
  • ( SSH-).
  • , , , . OWASP, .
  • , . , .
  • OAuth, OpenID, . Basic Authentication.
  • . , ( — ), X , Y .
  • — , — . , .
  • , , . ( — GitHub, AWS, Jenkins, ).

Resumen


. , Node.js-.

Estimados lectores! -, ?

Source: https://habr.com/ru/post/es419719/


All Articles