El protocolo implementado ya en todos los navegadores permite unas nuevas líneas de comunicación cliente-servidor bidireccionales que desde el punto de vista de la seguridad se deben tener en cuenta. Además, desde la administración de sistemas (que es lo que toco), sí existe la responsabilidad de diseñar una buena arquitectura que ayude a la seguridad de todo el conjunto aunque al final uno queda a merced del buen código del propio desarrollo.
Mirando desde el lado del cliente ya existen en la red ciertos documentos (véase Shekyan Toukharian – Hacking Websocket Slides) de cómo cualquier ejecución no autorizada sobre protocolos embebidos en el navegador, como puede ser javascript, pueden lanzar comunicaciones que vayan por estos canales bidireccionales que proporciona los websockets en background. Todo ello sin el conocimiento del usuario.
Por otro lado, desde el punto de vista del servidor, la perspectiva puede ser otra.
Como la comunicación entre el cliente (navegador) y el servidor es necesariamente directa para mantener esos canales, ciertas instalaciones acaban exponiendo sus servidores sobre los que se soportan los websockets. Realmente esto no sería mayor problema si se realizase correctamente pero sí que lo puede ser cuando dicha exposición, para permitir la comunicación, conlleva que el backend quede alcanzado desde fuera para que los navegadores, y desde ese momento todo Internet, puedan acceder a él.
Los inconvenientes son dos muy claros:
- No es posible la aplicación eficiente de reglas cuando son los navegadores de móviles, navegadores de portátiles y n-mil dispositivos de internet los que tienen que acceder al server vía websockets.
- Multitud de servidores necesitan de extensiones no instaladas en los ISPs para su uso.
Realmente hay cientos de arquitecturas pero para hoy vamos a elegir como ejemplo Nginx con Node.js y Sockect.io.
Las implementaciones de Socket.io son sencillas. Es autocontenido y el trabajo con sockets del propio Socket.io permite que se pueda optar por un acceso directo contra el puerto del Node.js. No obstante, si el despliegue de Node.js lo hacemos desde el backend (por un tema de intercomunicación y escalabilidad) exponemos un servicio que tal vez no era necesario. La alternativa tal vez podría ser optar por “encapsular”, y por tanto abstraer el acceso al Node.js a través de un segundo dominio y gracias a un nginx por ejemplo.
Nginx nos permite también una exquisita creación de hilos en caso de picos y nos proporciona un buen nivel de abstracción para websockets desde su versión 1.3.x (que no recomiendo porque anda con algún importante bug de seguridad). Así que nos metemos en la 1.4.1 ya que las versiones anteriores no soportan estas comunicaciones.
El proceso es sencillo. Únicamente vamos a necesitar un nombre de dominio adicional a nuestra config (si ya hubiera una). Esto nos permite de forma muy sencilla desmarcarnos del resto de funciones que esté realizando nginx (web, vídeo, etc etc) y usar la funcionalidad de websockets únicamente para ese nombre de dominio (servers en nginx).
Tenemos que crear un upstream para el control de los backends y creamos sock.midominio.es
Al final la implementación sería algo como:
upstream websockets_nodejs { server backend:9090; } server { listen 80; server_name sock.midominio.es; root /usr/local/app/sock/app/webroot; keepalive_timeout 512; location / { proxy_pass http://websockets_nodejs; proxy_redirect off; proxy_http_version 1.1; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } }
- server backend:9090: backend es un nombre de /etc/hosts y 9090 es el puerto por defecto donde se levanta nodejs.
- keepalive_timeout: mejor corto que largo por tema de recursos pero si el código no es capaz de controlar la conexión es posible indicar un número superior para evitar algunos comportamientos anómalos.
- El resto de líneas son de manual. Únicamente faltaría indicar en nuestro desarrollo como acceder al server que gestiona los websockets indicando nuestro nuevo dominio y si, todo por el puerto 80.
Evidentemente es posible configurarlo sobre SSL sin problemas:
upstream websockets_nodejs { server backend:9090; } server { listen 443; server_name sckts.midominio.es; root /usr/local/app/sock/app/webroot; index index.php; keepalive_timeout 512; ssl on; ssl_certificate /etc/nginx/server.crt; ssl_certificate_key /etc/nginx/server.key; ssl_session_timeout 5m; ssl_protocols SSLv2 SSLv3 TLSv1; ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP; ssl_prefer_server_ciphers on; location / { proxy_pass http://websockets_nodejs; proxy_redirect off; proxy_http_version 1.1; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } }
En nuestro entorno de pruebas ha ido de maravilla aunque siempre habrá alguien que podrá apuntar más datos interesantes sobre estas arquitecturas. En ese caso, bienvenido sea.
(Puedes seguirnos en Twitter: @SecurityArtWork)
Tomado de: http://www.securityartwork.es/2013/06/13/abstrayendo-websockets-ssl/
No hay comentarios:
Publicar un comentario