Generalidades
Nginx es un servidor web de alto rendimiento diseñado para servir aplicaciones escalables de alto rendimiento de manera eficiente y receptiva. Se puede utilizar para servir contenido estático, equilibrar la carga de solicitudes HTTP, proxy inverso FCGI/PSGI/USWGI y conexiones TCP arbitrarias. Dado esto, es importante poder configurar e implementar de forma segura las instalaciones de Nginx para proporcionar una interfaz web segura para tu aplicación y minimizar las superficies de ataque.
Asegurar el binario
Manténlo actualizado
La base de código central de Nginx (administración de memoria, manejo de sockets, etc.) es muy segura y estable, aunque las vulnerabilidades en el binario principal sí aparecen de vez en cuando. Por esta razón, es muy importante mantener Nginx actualizado. La mayoría de las distribuciones modernas de Linux no incluirán la última versión de Nginx en tus listas de paquetes predeterminadas, por lo que para instalar la última versión de Nginx a través de un paquete, es posible que deba agregar repositorios de paquetes adicionales a tu sistema. Consulta la documentación de Nginx a continuación para obtener detalles por distribución.
Compilar desde source
Como alternativa a la creación de paquetes, es posible crear Nginx desde la fuente. Hacerlo te permite ejecutar la última versión disponible del equipo de desarrollo de Nginx y permite configuraciones de seguridad adicionales. La construcción desde la fuente requiere algunos pasos. Primero, deberás asegurarte de tener los paquetes de sistema operativo necesarios para compilar desde la fuente.
[user@instance]$ sudo apt-get update && sudo apt-get install automake gcc \ libpcre3-dev zlib1g-dev make -y
A continuación, necesitarás el archivo de origen y su firma, los cuales puedes descargar desde el sitio oficial de Nginx. Este ejemplo usará la versión 1.12.2, la rama estable actual al momento de escribir este artículo:
[user@instance]$ wget -q https://nginx.org/download/nginx-1.12.2.tar.gz{,.asc}
También querrás tomar la clave de firma del desarrollador y verificar el contenido de tu descarga. Primero, necesitarás la clave de firma, que se puede descargar desde nginx.org:
[user@instance]$ curl -sS https://nginx.org/keys/mdounin.key | gpg --import gpg: directory `/home/ubuntu/.gnupg' created gpg: new configuration file `/home/ubuntu/.gnupg/gpg.conf' created gpg: WARNING: options in `/home/ubuntu/.gnupg/gpg.conf' are not yet active during this run gpg: keyring `/home/ubuntu/.gnupg/secring.gpg' created gpg: keyring `/home/ubuntu/.gnupg/pubring.gpg' created gpg: /home/ubuntu/.gnupg/trustdb.gpg: trustdb created gpg: key A1C052F8: public key "Maxim Dounin <mdounin@mdounin.ru>" imported gpg: Total number processed: 1 gpg: imported: 1 (RSA: 1) gpg: no ultimately trusted keys found
Ahora verifica la firma:
[user@instance]$ gpg --trusted-key 0x520A9993A1C052F8 --verify nginx-1.12.2.tar.gz{.asc,} gpg: Signature made Mon 01 2024 Apr 01:18:21 PM UTC using RSA key ID A1C052F8 gpg: key A1C052F8 marked as ultimately trusted gpg: checking the trustdb gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u gpg: Good signature from "Maxim Dounin <mdounin@mdounin.ru>"
Desde aquí, descomprime el tarball, compila Nginx e instálalo:
[user@instance]$ tar -zxf nginx-1.12.2.tar.gz [user@instance]$ cd nginx-1.12.2/ [user@instance]$ ./configure && make && sudo make install
Eliminar módulos innecesarios
De forma predeterminada, Nginx se compila con una serie de módulos que amplían su funcionalidad. Estos permiten que Nginx se extienda para realizar una serie de funciones, pero es poco probable que todos los módulos se utilicen en un servidor determinado. Se recomienda eliminar los módulos no utilizados para reducir el tamaño del binario compilado y reducir la superficie de ataque que Nginx presenta al mundo (por ejemplo, una vulnerabilidad encontrada en el proxy uwsgi no sería explotable contra un servidor que no aprovecha el módulo uswgi ). La eliminación de módulos se puede realizar en tiempo de compilación mediante el script de configuración. Por ejemplo:
[user@instance]$ ./configure --without-http_uwsgi_module
El script de configuración proporcionado con el script Nginx proporciona una gran cantidad de opciones en tiempo de compilación.
Asegurar las configuraciones
Los computadores son tan inteligentes como las personas que las usan. Nginx está diseñado para ser estable y seguro, pero solo será tan seguro como el usuario que lo configure. Una vez que Nginx está construido e instalado, es importante configurar el servidor para que sea lo más mínimo posible.
Ejecutar como un usuario sin privilegios
En seguridad, el principio de privilegio mínimo establece que a una entidad no se le debe otorgar más permiso de lo necesario para lograr tus objetivos dentro de un sistema dado. En el contexto de tu servidor web Nginx, esto significa bloquear Nginx para que se ejecute solo con los permisos necesarios para ejecutar.
Primero, crea un nuevo usuario sin privilegios sudo. Luego, puedes configurar Nginx para que se ejecute como un usuario del sistema sin privilegios (por ejemplo, no el usuario root o un usuario con privilegios de sudo). Esto se hace mediante la directiva de user en el archivo de configuración /etc/nginx/nginx.conf. Al principio puede parecer comentado:
#user nobody;
Descomentalo y cambia el usuario al nuevo usuario que creaste.
user nginx;
Deshabilitar Server Tokens
La especificación HTTP recomienda (pero no requiere) que los servidores web se identifiquen mediante el encabezado del Server. Históricamente, los servidores web han incluido su información de versión como parte de este encabezado. Revelar la versión de Nginx en ejecución puede ser indeseable, particularmente en entornos sensibles a la revelación de información. Nginx se puede configurar para que no muestre su versión en el encabezado del servidor editando el archivo Nginx.conf con lo siguiente:
server_tokens off;
Ocultar encabezados de proxy ascendentes
Del mismo modo, cuando se utiliza Nginx para enviar solicitudes desde un servidor ascendente (como una instancia PHP-FPM), puede ser beneficioso ocultar ciertos encabezados enviados en la respuesta ascendente (por ejemplo, la versión de PHP en ejecución). Por ejemplo, considera la siguiente respuesta de un servidor Nginx que ejecuta una aplicación PHP:
[user@instance]$ curl -I https://example.com HTTP/1.1 200 OK Server: nginx Content-Type: text/html; charset=UTF-8 Connection: keep-alive Vary: Accept-Encoding X-Powered-By: PHP/8.2
Revelar la versión de PHP puede ser indeseable; Las configuraciones de Nginx hacen que esto sea fácil de ocultar con la directiva proxy_hide_header:
proxy_hide_header X-Powered-By;
Reenviar la solicitud al mismo servidor ahora tendría este resultado:
[user@instance]$ curl -I https://example.com HTTP/1.1 200 OK Server: nginx Content-Type: text/html; charset=UTF-8 Connection: keep-alive Vary: Accept-Encoding
Agregar encabezados de seguridad
Además de enmascarar información confidencial, Nginx también se puede usar para inyectar encabezados con implicaciones positivas para la seguridad en las respuestas. Un ejemplo trivial es agregar un encabezado X-Frame-Options para evitar ataques de clickjacking:
add_header X-Frame-Options SAMEORIGIN;
Esta directiva también se puede usar para agregar encabezados arbitrarios a tu antojo.
Restringir el acceso por medio de IP
Las áreas sensibles de los sitios web, como los paneles de control de administración, deben tener controles de acceso estrictos. Nginx facilita incluir en la lista blanca el acceso IP a ciertas ubicaciones de tu sitio web y negar el tráfico a todas las demás direcciones IP:
location /wp-admin { # allow access from one IP and an additional IP range, # and block everything else allow 1.2.3.4; allow 192.168.0.0/24; deny all; }
Restringir acceso por contraseña
El acceso a ciertas ubicaciones también se puede configurar mediante credenciales basadas en contraseña, utilizando el mismo formato que usan los archivos .htaccess y .htpasswd de Apache:
location /wp-admin {
auth_basic "Admin Area";
auth_basic_user_file /path/to/.htpasswd;
}
Donde los contenidos de .htpasswd se ven algo así:
user1:password1 user2:password2 user3:password3
Asegurar SSL/TLS
Nginx sobresale en el servicio de tráfico SSL/TLS. Configurar tu servidor web para proporcionar configuraciones SSL/TLS seguras para los clientes es esencial para mantener una conexión segura.
Como nota, se recomienda encarecidamente que el tráfico cifrado utilice solo protocolos TLS más nuevos, en lugar de SSL heredado. Ambas versiones de SSL ampliamente disponibles en la actualidad (SSLv2 y SSLv3) tienen fallas de seguridad graves y nunca deben usarse en entornos de producción. Históricamente, las directivas asociadas con la configuración de SSL/TLS en Nginx tienen el prefijo ssl. Sin embargo, para promover el uso de protocolos de seguridad modernos, este tutorial usa el término TLS cuando hace referencia al tráfico cifrado (HTTPS) y ssl cuando se aplica a directivas de configuración específicas de Nginx.
Encender TLS
Para servir tráfico cifrado, SSL/TLS debe estar habilitado para tu servidor. Afortunadamente, las conexiones cifradas se pueden habilitar/deshabilitar por servidor en Nginx:
server { # regular server listening for HTTP traffic listen 80; } server { # server listening for SSL traffic on port 443; listen 443 ssl; }
Habilitar cifrados TLS sólidos
De forma predeterminada, Nginx permite utilizar una amplia variedad de cifrados criptográficos en las conexiones TLS. Algunos de estos cifrados son ofertas heredadas que son débiles o propensas a ataques y no deben utilizarse. DreamHost recomienda usar los conjuntos de cifrado modernos o intermedios descritos por Mozilla aquí:
La lista moderna de cifrados es más sólida, pero causará problemas de conectividad para plataformas más antiguas como Internet Explorer o Windows XP. Además, se recomienda que el servidor prefiera qué cifrados se pueden usar:
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS'; ssl_prefer_server_ciphers on;
Habilitar el almacenamiento en caché de la sesión TLS
Abrir una nueva conexión TLS a un servidor es muy costoso como resultado de los protocolos criptográficos involucrados. Para mantener un entorno de alto rendimiento, se recomienda almacenar en caché las conexiones TLS existentes para que cada nueva solicitud de un cliente/navegador no necesite realizar el protocolo de enlace TLS completo:
ssl_session_cache shared:SSL:50m; ssl_session_timeout 5m;
Usar parámetros personalizados de Diffie-Hellman
El ataque Logjam, publicado en 2015, mostró que era posible que los atacantes (como los actores del estado-nación) rompieran el intercambio de claves Diffie-Hellman, utilizado para implementar el secreto hacia adelante (esencialmente, otra capa por encima de los mensajes cifrados existentes). Mitigar este ataque es posible en Nginx calculando un conjunto único de parámetros Diffie-Hellman y configurando Nginx para usar este valor:
[user@instance]$ openssl dhparam 2048 > /path/to/dhparam
Desde aquí solo necesitas decirle a Nginx que use los valores personalizados que generaste anteriormente:
ssl_dhparam /path/to/dhparam;
Para obtener más información sobre el ataque Logjam, consulta el sitio "Weak Diffie-Hellman and the Logjam Attack" aquí:
Forzar todas las conexiones a través de TLS
Las comunicaciones cifradas solo son útiles cuando están realmente en uso. Si lo deseas, es posible decirle a los navegadores que solo usen conexiones TLS para tu sitio. Esto se logra con el encabezado Strict-Transport-Security, que se puede agregar en tu configuración Nginx de esta manera:
add_header Strict-Transport-Security max-age=15768000;
También puedes configurar Nginx para enviar un redireccionamiento 301 para solicitudes HTTP de texto plano a la versión TLS de tu sitio:
server { listen 80; server_name example.com; return 301 https://$host$request_uri; } server { listen 443 ssl; server_name example.com; # the rest of the appropriate server block below... }
Medidas de seguridad adicionales
Más allá de los conceptos básicos de instalar un binario Nginx seguro, bloquear el acceso a áreas sensibles de tu sitio y servir correctamente las conexiones TLS, existen algunos pasos adicionales que se pueden tomar para el usuario más consciente de la seguridad.
Instalar un WAF
Un WAF (firewall de aplicaciones web) es un software diseñado para inspeccionar el tráfico HTTP/HTTPS, rechazar solicitudes maliciosas y, en general, actuar como una capa adicional de seguridad en tu pila web. Un WAF configurado correctamente puede proteger tu sitio de ataques SQLi, XSS, CSRF y DDoS, además de proporcionar mitigación de ataques de fuerza bruta y parches de amenazas de día cero. Hay algunas opciones WAF de código abierto disponibles para Nginx:
ModSecurity
ModSecurity, originalmente escrito como WAF para servidores Apache, es el estándar de facto para las soluciones WAF de código abierto. El trabajo reciente en el proyecto ha cambiado el enfoque hacia el soporte de Nginx; Para obtener más información y detalles sobre la instalación y configuración, consulta la página de inicio del proyecto y la página de GitHub aquí:
Naxsi
Naxsi es una alternativa ligera a ModSecurity, diseñada como un módulo Nginx nativo y se centra en la prevención XSS/SQLi en los parámetros de solicitud.
OpenResty
Para los usuarios del paquete OpenResty que buscan un WAF programable y de alto rendimiento, consulta lua-resty-waf, que busca proporcionar un motor de reglas compatible con ModSecurity integrado en el ecosistema Nginx + LuaJIT.
Automated Log Analysis + Monitoring
Los programas como Fail2Ban se pueden usar para monitorear el acceso a Nginx y los registros de errores, buscar patrones de ataque y tomar acciones contra el cliente atacante (como eliminar direcciones IP, informar sobre comportamiento malicioso al propietario de la IP, etc.). Fail2Ban es extensible, lo que te permite escribir tu propio patrón de búsqueda y comportamiento de respuesta. Para obtener más información y detalles sobre la instalación y configuración, consulte la página de GitHub del proyecto aquí:
Limitar el tráfico de entrada a través de IPTables
Más allá de proteger Nginx en sí, es importante proteger el entorno de host utilizado para alojar tu servidor web. Bloquear el acceso a cosas como SSH puede aumentar en gran medida la seguridad del host al evitar intentos de intrusión. Un enfoque común es incluir en la lista blanca las IP conocidas que accederán a tu host a través de SSH y negar el resto del tráfico del puerto 22, o utilizar un cuadro de salto que filtre estrictamente el acceso al shell. También puedes hacer esto configurando un grupo de seguridad personalizado para tu instancia. Para obtener más información, consulta el artículo de DreamHost aquí: