Nuevas tecnologías / viejas vulnerabilidades – Node.js (I)

Cada día vemos aparecer en el mercado nuevas tecnologías que permiten nuevas formas de desarrollo para la innovación. Sin ir más lejos, en el mundo de la web, la innovación conduce a la popularidad, haciendo que sitios como Google, Twitter y Facebook triunfen.

El principal problema de las nuevas tecnologías es que, aún teniendo la posibilidad de evitar problemas de seguridad conocidos, desafortunadamente, la mayoría de las veces esta no es el objetivo principal, por lo que estos errores se vuelven a repetir una y otra vez. Además de esto, las nuevas tecnologías también tienden a inventar nuevas clases de vulnerabilidad, o nuevas formas para explotar problemas de seguridad conocidos.

Un ejemplo práctico de que lo estoy diciendo es Node.js

¿Qué es Node.js?

El 8 de noviembre de 2009, Ryan Dahl presentó Node.js como un entorno de desarrollo en JavaScript de lado de servidor, totalmente asíncrono y orientado a eventos. Usa el motor de JavaScript V8 de Google, dotando a este entorno de una gran velocidad y agilidad, y sumando además una capacidad de Entrada/Salida realmente ligera y potente. Esto permite construir aplicaciones altamente escalables y escribir código que maneje miles de conexiones simultáneas en un solo equipo. Entre las compañías que usan esta tecnología destacamos LinkedIn, eBay, Yahoo!, Microsoft y nosotros mismos (S2 Grupo).

¿Qué es el V8?

V8 es el motor de JavaScript que Google usa en su navegador Chrome. Un motor normal de JavaScript, interpreta el código y lo ejecuta. El V8 es ultra-rápido, está escrito en C++ y se puede descargar por separado e incorporarlo a cualquier aplicación. Así nace Node.js, cambiando el propósito por el que se creó V8 y usándolo en el lado del servidor.

¿Para qué JavaScript en el lado del servidor?

La meta principal de Node.js es la de proporcionar una manera fácil de construir programas de red escalables. En lenguajes como Java™ y PHP, cada conexión genera un nuevo hilo con su respectiva reserva de memoria. Esto limita la cantidad de peticiones concurrentes que puede tener un sistema. Por ejemplo, un servidor muy común es Apache, el cual crea un nuevo hilo por cada conexión cliente-servidor. Esto funciona bien con pocas conexiones, pero a partir de 400 conexiones simultáneas, el número de segundos para atender las peticiones crece considerablemente. Así pues, Apache funciona bien en entornos clásicos, pero no es el mejor servidor para lograr máxima concurrencia.

Node.js resuelve este problema cambiando la forma en que se realiza una conexión con el servidor. En lugar de generar un nuevo hilo de SO para cada conexión, cada conexión entra en el bucle de ejecución y dispara el evento dentro del “pool” de trabajadores. De esta forma, nunca se quedará en punto muerto, dado que no se permiten bloqueos y no se bloquea directamente las llamadas de E/S, permitiendo así miles de sesiones concurrentes (en un sistema UNIX este límite puede rondar por las 65.000 conexiones).

¿Cuándo usar Node.js?

Node.js está extremadamente bien diseñado para situaciones en la que se espere una gran cantidad de tráfico y donde la lógica del servidor y el procesamiento requeridos sean sencillas para dar una respuesta lo antes posible. Node.js es especialmente bueno en aplicaciones web que necesiten conexiones persistentes con el navegador del cliente. Usando ciertas librerías, en concreto socket.io, puedes hacer que una aplicación envíe datos al usuario en tiempo real; es decir, que el navegador mantenga la conexión siempre abierta y reciba continuamente nuevos datos cuando se requiera. Hay que entender que, aunque se puede usar como servidor de páginas web “clásicas”, no se diseñó para ese fin. Algunos ejemplos de uso:

  • Servicios Web que proporcionen APIs RESTful.
  • “Timelines” de Twitter.
  • Aplicaciones de única página.
  • Videojuegos de varios jugadores.
  • Aplicaciones tiempo real (Chats, cuadros de mando).

Node.js dispone de un gestor de módulos (librerías, plugins, frameworks) npm (Node Package Manager), que amplían la funcionalidad de este y facilitan tareas. En otro futuro post trataremos de hablar de varios frameworks típicos de Node.js, en concreto de Fusker, que es un firewall de aplicación que previene y maneja una gran cantidad de ataques en Node.js.

Entre estas librerías, destacaremos la ya nombrada Socket.io, que es una librería que nos permite manejar eventos en tiempo real mediante una conexión TCP. Socket.io la cual tiene como objetivo hacer que las aplicaciones en tiempo real sean posibles en todos los navegadores y dispositivos móviles, borrando las diferencias entre los mecanismos de transporte (WebSocket, Adobe® Flash® Socket, AJAX long polling, AJAX multipart streaming, Forever Iframe, JSONP Polling).

Estas comunicaciones se pueden poner detrás de un servidor clásico como podría ser Apache o Nginx tal y como explica Vicente Domínguez en su post.

Un poco de código

Para comenzar, podemos descargar Node.js de la página de la aplicación http://nodejs.org/, para varios sistemas. En mi caso he realizado las pruebas con la última versión (tar.gz) para Linux.

Una vez descargado y funcionando vamos a comenzar escribiendo la aplicación en un fichero js. Editamos un archivo “ejemplo-1.js” y escribimos el siguiente código:

var http = require('http');
http.createServer(function (req, res) {
     res.writeHead(200, {'Content-Type': 'text/plain'});
     res.end('Hello World\n');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');

Ahora desde la consola lanzamos la aplicación:

$ node ejemplo-1.js

Para los que no estéis familiarizados con JavaScript, existen multitud de tutoriales en internet, no es el propósito de este post explicar línea por línea que hacen los programas. En resumen, vemos que “ejemplo-1.js” crea un servidor que escucha en el puerto 1337 y, ante cualquier petición a http://127.0.0.1 devuelve un texto plano “Hello World”.

En el siguiente post, apoyándome en la presentación que hizo Sven Vetsch (Redguard AG – www.redguard.ch) para el Application Security Forum de 2012, trataré de mostrar cómo pueden afectar las clásicas vulnerabilidades (XSS reflejado, inyección de JS en el servidor, ejecución de código remoto).

Comments

  1. Muy bien explicado compi. Ansioso por leer la siguiente entrada. Node.js rules ;)

  2. Tiene una pinta excelente. ¿Sabes cuántos hilos podría manejar esta interfaz en un sistema non UNIX?

    Saludos

  3. En teoría un sistema UNIX podría realizar 65.000 conexiones, pero en la realidad la cifra depende otros factores. Con el comando sysctl podemos ver el número máximo de hilos, pero tampoco se con exactitud si sólo son para el kernel o para todas las aplicaciones:

    #sysctl -a |grep threads
    kernel.threads-max = 29732
    kernel.slow-work.min-threads = 2
    kernel.slow-work.max-threads = 4
    vm.nr_pdflush_threads = 0

  4. El post no se trataba sobre las vulnerabilidades de node.js ?????

    Por lo que veo antes le estas echando flores.

    PD: no encuentro el “siguiente post”

  5. Hola Julián… no lo busques porque no lo he terminado. Y si, le hecho flores, porque a pesar de lo que digan, mi experiencia con él es muy buena.
    Espero no tardar mucho en terminarlo, pero de momento me está siendo imposible hacer las pruebas que quiero poner.

    Un saludo y gracias por comentar.

  6. Buenas, con vuestro permiso vamos a utilizar la imagen que aparece en vuestra entrada para una presentación sin ánimo de lucro ni derechos reclamados sobre ella.

Trackbacks

  1. […] Cada día vemos aparecer en el mercado nuevas tecnologías que permiten nuevas formas de desarrollo para la innovación. Sin ir más lejos, en el mundo de la web, la innovación conduce a la popularidad…  […]