Este artículo explica cómo configurar un servidor Web Nginx de forma segura, que acepte pedidos sólo a través de TLS (redireccionando de forma automática todos los pedidos HTTP a HTTPS), de forma que todo el tráfico entre los clientes y el servidor sea encriptado utilizando sólo los protocolos TLS más fuertes. Para ello se deshabilitará la compresión SSL para evitar el ataque CRIME, también SSLv3 e inferiores debido a sus vulnerabilidades, y se configurará una suite de cifrado fuerte que permita Perfect Forward Secrecy (PFS) cuando sea posible.



Por defecto, cuando se instala desde la colección de ports, el archivo de configuración de Nginx en FreeBSD está ubicado en la ruta /usr/local/etc/nginx/nginx.conf. Se recomienda hacer una copia de seguridad de este archivo antes de modificarlo.

Es posible definir un servidor que escuche en ambos puertos 80 (HTTP) y 443 (HTTPS), con el objetivo de no tener que replicar la configuración:

server {

  listen 80; 
  listen 443 default ssl;

Claro está que, para proveer SSL/TLS, se necesita un certificado. Es posible crear un certificado SSL autofirmado en dos simples pasos, el cual sólo es útil a modo de prueba, pues provee seguridad pero no confianza. También es posible obtener un certificado SSL gratis. De cualquier forma, se debe especificar la ruta a la calve privada y al certificado utilizando las siguientes líneas:

ssl_certificate      /usr/local/etc/nginx/linuxito.com.crt;
ssl_certificate_key  /usr/local/etc/nginx/linuxito.com.key;

El ataque CRIME aprovecha una vulnerabilidad en el mecanismo de compresión de SSL. El mismo se encuentra desactivado por defecto en las versiones de Nginx 1.1.6+/1.0.9+ (si se utiliza OpenSSL 1.0.0+) y 1.3.2+/1.2.2+ (si se utilizan versiones de OpenSSL anteriores). Si no se alcanzan estas versiones de Nginx u OpenSSL, y el sistema operativo no tiene un backport para la versión de OpenSSL utilizada, no quedará otra alternativa que recompilar OpenSSL sin soporte para zlib. Esto deshabilitará el uso de OpenSSL con el método de compresión DEFLATE. Sin embargo aún se puede utilizar el método regular de compresión DEFLATE de HTML, es decir, esta configuración de seguridad no perjudica la eficiencia o rendimiento del servidor Web.

El protocolo SSLv2 es inseguro, por lo que debe deshabilitarse. También SSLv3 para evitar ataques de tipo downgrade a TLS 1.0, lo que permite a un atacante forzar el uso de SSLv3 y así deshabilitar Forward Secrecy. Para ello, la variable ssl_protocols debe tener la siguiente configuración:

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

El concepto de Forward Secrecy asegura la integridad de una clave de sesión en el evento que una clave de largo plazo sea comprometida (por ejemplo, la clave privada de un certificado). Esto se acomete forzando la creación de una nueva clave para todas y cada una de las nuevas sesiones. De esta forma, si la clave privada se compromete, no puede ser utilizada para descifrar comunicaciones previas. Las suites de cifrado que proveen Forward Secrecy son aquellas que utilizan alguna forma efímera del mecanismo de intercambio de claves Diffie-Hellman. La desventaja es la sobrecarga de procesamiento, la cual puede ser mejorada utilizando una de las variantes de curva elíptica (ECDHE).

A fin de proveer Forward Secrecy a los clientes más modernos, promover el uso de TLS 1.2 y proveer algo de seguridad para clientes que aún utilizan Windows XP; se deben especificar las siguientes suites de cifrado en la variable de configuración ssl_ciphers:

ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4';

Para poder utilizar TLS 1.2 se necesita una versión de OpenSSL 1.0.1c o posterior.

La configuración anterior prefiere el uso de mecanismos de intercambio de claves efímeros que provean PFS. Se favorece el uso del mecanismo Elliptic Curve Diffie-Hellman (ECDHE) sobre la versión tradicional (DHE), ya que el primero es menos intensivo en tiempo de CPU y provee un mismo nivel de seguridad. Sin embargo se soportan ambos, por cuestiones de compatibilidad. Para proveer identidad y cifrado de clave pública se soporta sólo RSA. La mayoría de los certificados son RSA (los certificados DSA son poco comunes). Para cifrar los mensajes utilizando criptografía simétrica se prefiere AES, especialmente la versión GCM (pues es más eficiente y no susceptible al ataque BEAST), y además se prefiere utilizar claves de 256 bits sobre 128. Finalmente, para proveer autenticación de mensajes (digest/hashing) se favorece el uso de SHA-2 de longitud 256 o 384 bytes, y se deshabilitan protocolos inseguros, no autenticados o no cifrados.

Cuando se elige una suite de cifrado durante el handshake, normalmente se utiliza la preferencia del cliente. Para utilizar la preferencia del servidor (configurada anteriormente, la cual es mucho más segura) agregar las siguientes líneas adicionales a la configuración:

ssl_prefer_server_ciphers on;
ssl_session_cache builtin:1000 shared:SSL:10m;

Todas las versiones de Nginx desde la 1.4.4 se basan en OpenSSL para los parámetros de entrada a Diffie-Hellman. Esto significa que se utilizarán los valores por defecto de OpenSSL, los cuales desafortunadamente incluyen una clave de 1024 bits para el intercambio de claves. Para generar un parámetro más fuerte para Diffie-Hellman, ejecutar los siguientes comandos en FreeBSD:

# mkdir -p /usr/local/etc/ssl/certs
# cd /usr/local/etc/ssl/certs
# openssl dhparam -out dhparam.pem 2048

Luego indicar el uso de este parámetro mediante la siguiente directiva, en la configuración de Nginx:

ssl_dhparam /usr/local/etc/ssl/certs/dhparam.pem;

El protocolo OCSP (Online Certificate Status Protocol) se utiliza para verificar el estado de revocación de un certificado de forma segura y confiable. Por otro lado, la característica de stapling es una mejora al protocolo que envía la respuesta OCSP (cacheada) junto con el certificado del servidor, eliminando así la necesidad de que el cliente deba verificar luego la validez del mismo contra la CA que lo emite. Esto tiene el objetivo de reducir ancho de banda y mejorar la eficiencia. Para habilitar esta optimización, agregar las siguientes líneas:

ssl_stapling on;
ssl_stapling_verify on;

Lógicamente esta configuración no tiene sentido cuando se utiliza un certificado autofirmado, pues no es posible contactar CA alguna.

Si se desea maximizar la seguridad de los clientes, se recomienda utilizar HSTS (HTTP Strict Transport Security), lo cual instruye a los navegadores a comunicarse con el sitio sólo a través de HTTPS. Para ello, agregar la siguientes configuración:

add_header Strict-Transport-Security max-age=63072000;
add_header X-Frame-Option DENY;
add_header X-Content-Type-Options nosniff;

A su vez se prohíbe el renderizado de páginas dentro de tags <frame>, <iframe> u <object>, con el objetivo de evitar ataques de tipo clickjacking. También se indica a Internet Explorer y Google Chrome que no traten de interpretar el tipo MIME de una respuesta más allá del declarado en el header HTTP, para evitar ataques de tipo drive-by durante las descargas.

Para mayor información sobre headers HTTP, revisar la lista de headers HTTP útiles de OWASP.

La siguiente configuración no es específica de seguridad y es opcional, define los tamaños de los buffers y timeouts para HTTP:

client_body_buffer_size 1K;
client_header_buffer_size 1k;
client_max_body_size 1k;
large_client_header_buffers 2 2k;
client_body_timeout 10;
client_header_timeout 10;
send_timeout 10;

Para mayor detalle acceder a la documentación del módulo ngx_http_core_module.

Luego es recomendable denegar peticiones hacia nombres de host no definidos y restringir el uso de HTTP sólo a los métodos GET, POST y HEAD:

if ($host !~ ^(linuxito.com|www.linuxito.com)$ ) {
    return 444;
}
if ($request_method !~ ^(GET|POST|HEAD)$ ) {
    return 444;
}

Por último, es posible forzar el uso de HTTPS en Nginx redirigiendo automáticamente todo el tráfico HTTP:

root /usr/local/www/;
index index.html index.htm;
server_name linuxito.com;
listen 80 default;
return 301 https://$server_name$request_uri;

Al finalizar la configuración, reiniciar el servidor Nginx:

# service nginx restart

Es posible verificar el nivel de seguridad alcanzado utilizando la herramienta SSL Server Test, tal como expliqué anteriormente en el artículo Hardening de SSL/TLS en servidores HTTPS.

Fuente: BSD Now - Building a hardened, feature-rich webserver.


Tal vez pueda interesarte


Compartí este artículo