HTTP seguro: SSL bajo Linux
SSL (Secure Socket Layer) es un protocolo cliente-servidor que permite conexiones seguras (confidencialidad, autentificación e integridad) a cualquier protocolo basado en TCP/IP, lo que se denomina "entunelar". Se usa normalmente con HTTP (formando HTTPS), para asegurar páginas web de comercio electrónico, entidades bancarias, etc. En Linux se utiliza la implementación SSL de OpenSSL (openssl.org, paquete openssl, archivo de configuración /etc/ssl/openssl.cnf).
Equivalencias en Windows: MS-IIS.
Cómo funciona SSL
El funcionamiento de SSL es muy similar a SSH:
- El cliente solicita una conexión al servidor SSL, que escucha por defecto en el puerto 443 TCP.
- Cliente y servidor comprueban las versiones del protocolo soportadas.
- Cliente y servidor acuerdan los algoritmos a utilizar.
- El servidor envía su clave pública al cliente.
- El cliente genera la "clave de sesión" (válida sólo para esa sesión) y la envía al servidor encriptada con la clave pública del servidor.
- A partir de ese momento, todo el tráfico se encripta con cifrado simétrico con la "clave de sesión".
- Las claves públicas admiten certificados en los que se basa la autentificación.
- Normalmente sólo se autentifica el servidor, mientras que el cliente se mantiene sin autentificar.
- Se utiliza el valor hash de los datos transmitidos para garantizar la integridad de los mismos.
Servidor web con soporte SSL
Veamos paso a paso cómo implementar conexiones seguras SSL en un servidor web.
- Dominio
Necesitamos un dominio registrado, por ejemplo domain.com
- Generar un par Clave privada RSA / Clave pública CSR
Generamos la clave privada RSA (archivo domain.com.key) y la clave pública CSR (Certificate Signing Request, solicitud de certificado, archivo domain.com.csr). La clave privada es usada como input para generar la clave pública CSR. Para obtener un certificado hay que hacer una solicitud CSR, que contiene la clave pública junto con la información sobre el propietario del certificado.
# openssl req -newkey rsa:2048 -nodes -subj "/C=GB/ST=Yorks/L=York/O=MyCompany Ltd./OU=IT/CN=domain.com" -keyout domain.com.key -out domain.com.csr
Cuando el certificado es para un servidor puede ser conveniente que la clave privada no tenga password, ya que de lo contrario tendremos que introducirlo cada vez que reiniciemos el servidor. Para ello utilizaremos la opción -nodes.
Mediante el paramétro -subj suministramos los datos para el campo DN (Distinguished Name), que contiene información sobre el propietario del certificado:
- C: Country Name (2 letter code).
- ST: State or Province Name.
- L: Locality Name.
- O: Organization Name.
- OU: Organizational Unit Name.
- CN: Common Name.
Una vez creada la solicitud de certificado CSR podemos comprobar la información contenida en domain.com.csr utilizando:
# openssl req -noout -text -in domain.com.csr
Los certificados soportan el uso de wildcards, útiles para asegurar múltiples subdominios. Si usamos en la solicitud de certificado CSR domain.com aseguraremos el dominio domain.com pero no www.domain.com. En cambio, si utilizamos *.domain.com aseguraremos todos los subdominios (pero no domain.com).
# openssl req -newkey rsa:2048 -nodes -subj "/C=GB/ST=Yorks/L=York/O=MyCompany Ltd./OU=IT/CN=*.domain.com" -keyout domain.com.key -out domain.com.csr
- Certificado SSL
Ahora necesitamos obtener el certificado SSL (domain.com.crt) para lo cual debe firmarlo una CA (Certification Authority, Autoridad Certificadora).
- Certificado SSL de una CA reconocida
Los navegadores incorporan de origen un listado de CA reconocidas, de manera que si nuestro certificado va firmado por una de ellas el navegador lo aceptará directamente como válido. El inconveniente es que estas CA reconocidas son empresas que cobran por emitir sus certificados, como Go Daddy (20 $/año) o VeriSign (350 $/año). Existen también CA gratuitas, como CAcert, pero no están reconocidas por los navegadores.
Así pues, si no queremos que nuestros usuarios reciban un mensaje de alerta al entrar en nuestro sitio web SSL tendremos que adquirir un certificado de pago en una CA reconocida. Para ello, enviaremos la solicitud de certificado CSR (domain.com.csr) a la CA, quien, tras verificar que somos los propietarios del sitio web, nos remitirá el certificado con su firma (domain.com.crt).
En ocasiones, como medida de seguridad, la CA no firma nuestro certificado con su certificado raíz sino con un certificado intermedio, que es un certificado subordinado al certificado raíz. En ese caso recibiremos de la CA el certificado intermedio (CA_issuing.crt).
- Certificado SSL de nuestra propia CA
Si sólo queremos asegurar una página de login, podemos crear nuestra propia CA para validar nuestro certificado. Se crean los archivos mi_ca.key (clave privada RSA de nuestra CA) y mi_ca.pem (clave pública auto-firmada de nuestra CA):
# openssl req -x509 -newkey rsa:2048 -nodes -subj "/C=GB/ST=Yorks/L=York/O=MyCA/OU=IT/CN=MyCA" -keyout mi_ca.key -out mi_ca.pem
Emitimos el certificado. Se crean los archivos domain.com.crt (el certificado) y mi_ca.srl (CA serial number file):
# openssl x509 -CA mi_ca.pem -CAkey mi_ca.key -req -in domain.com.csr -days 3650 -sha1 -CAcreateserial -out domain.com.crt
Podemos comprobar los datos contenidos en mi_ca.pem con:
# openssl x509 -noout -text -in mi_ca.pem
Un certificado de nuestra propia CA no puede usarse en producción, ya que como no está firmado por una autoridad certificadora reconocida, en el browser obtendremos un mensaje de alerta:
www.domain.com usa un certificado de seguridad no válido. No se confía en el certificado porque el emisor es desconocido.
- Certificado SSL auto-firmado
Para asegurar la página de login de nuestra aplicación podemos generar un certificado auto-firmado:
# openssl x509 -req -days 365 -in domain.com.csr -signkey domain.com.key -out domain.com.crt
Al igual que ocurre con los certificados de nuestra propia CA, un certificado auto-firmado no puede usarse en producción, ya que como no está firmado por una autoridad certificadora reconocida obtendremos en el browser un mensaje de alerta:
www.domain.com usa un certificado de seguridad no válido. No se confía en el certificado porque está autofirmado.
Una vez obtenido el certificado, para comprobar los datos contenidos en domain.com.crt haremos:
# openssl x509 -noout -text -in domain.com.crt
- Certificado SSL de una CA reconocida
- Certificado en formato PEM
Ahora vamos a crear el archivo PEM (domain.com.pem), que no es más que el archivo .key y a continuación el archivo .crt concatenados:
# cat domain.com.key domain.com.crt > domain.com.pem
- Archivos
Estos serán los archivos que tendremos en /etc/<webserver>/domain.com/ (/etc/lighttpd/domain.com/ si usamos Lighttpd, /etc/apache/domain.com/ si usamos Apache):
- domain.com.key: clave privada RSA.
- domain.com.csr: solicitud de certificado, contiene la clave pública.
- domain.com.crt: certificado SSL de nuestro dominio.
- domain.com.pem: clave privada RSA y certificado x509 concatenados.
- CA_issuing.crt: certificado intermedio subordinado al certificado raíz de la CA.
Hay que establecer en los archivos los permisos adecuados, que el propietario sea el usuario que ejecuta el webserver y que sólo puedan ser accedidos por él.
- Servidor web
- Lighttpd
- Primero nos aseguramos de que Lighttpd soporta SSL:
# lighttpd -v lighttpd-1.4.19 (ssl) - a light and fast webserver
- A continuación editamos /etc/lighttpd/lighttpd.conf y configuramos SSL para nuestro dominio
(si la CA no utiliza certificado intermedio no necesitaremos la directiva ssl.ca-file):
$SERVER["socket"] == ":443" { ssl.engine = "enable" ssl.pemfile = "/etc/lighttpd/domain.com/domain.com.pem" ssl.ca-file = "/etc/lighttpd/domain.com/CA_issuing.crt" server.name = "domain.com" server.document-root = "/var/www/domain.com/" }
- Para activar los cambios reiniciamos Lighttp (nos pedirá el password de la clave privada, si tiene):
# /etc/init.d/lighttpd restart Password:
- Primero nos aseguramos de que Lighttpd soporta SSL:
- Apache
- Primero instalamos Apache con soporte SSL (paquete apache-ssl). Si ya tenemos Apache instalado, es suficiente con instalar el módulo SSL (paquete libapache-mod-ssl).
- Editamos /etc/apache/httpd.conf para que Apache también escuche en el puerto 443:
Listen 443
- Creamos en /etc/apache/httpd.conf un virtual host para el puerto 443
(si la CA no utiliza certificado intermedio no necesitaremos la directiva SSLCertificateChainFile):
NameVirtualHost *:443 <VirtualHost *:443> ServerAdmin webmaster@localhost DocumentRoot /var/www/domain.com SSLEngine on SSLCertificateFile /etc/apache/domain.com/domain.com.pem SSLCertificateKeyFile /etc/apache/domain.com/domain.com.key SSLCertificateChainFile /etc/apache/domain.com/CA_issuing.crt ServerName domain.com <Directory /var/www/domain.com> Options Indexes FollowSymLinks MultiViews AllowOverride None Order allow,deny allow from all </Directory> </VirtualHost>
- Para activar los cambios reiniciamos Apache (nos pedirá el password de la clave privada, si tiene):
# /etc/init.d/apache restart
- Lighttpd
- Comprobaciones
- Comprobaremos que nuestro webserver está escuchando en el puerto 443:
# netstat -ntulp Active Internet connections (only servers) Proto R S Local Address Foreign Address State PID/Program name ... tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN 4367/mysqld tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN 19436/lighttpd tcp6 0 0 :::80 :::* LISTEN 20249/lighttpd tcp6 0 0 :::22 :::* LISTEN 4277/sshd ...
- Abriremos el puerto 443 en nuestro firewall (iptables).
- En las URLs de las páginas seguras utilizaremos el esquema:
https://www.domain.com
en vez de:
http://www.domain.com
- Comprobaremos que nuestro webserver está escuchando en el puerto 443:
Certificado SSL auto-firmado multi-dominio
Veamos cómo implementar conexiones seguras SSL en un servidor web multi-dominio (Name-Based Virtual Host) con una única IP. Utilizaremos para ello un único certificado SSL con extensiones X509v3 y múltiples campos subjectAltName.
- Haremos una copia del archivo de configuración de SSL:
/etc/ssl/openssl.cnf
en nuestro directorio de trabajo: /etc/<webserver>/ssl/openssl.cnf
para personalizarlo. - Editamos openssl.cnf y descomentamos la línea:
req_extensions = v3_req # The extensions to add to a certificate request
- ... y añadimos los dominios que queremos asegurar (utilizaremos wildcards
para asegurar todos los subdominios):
[ v3_req ] subjectAltName = @alt_names [alt_names] DNS.1 = *.domain1.com DNS.2 = domain1.com DNS.3 = *.domain2.com DNS.4 = domain2.com DNS.5 = *.domain3.com DNS.6 = domain3.com
- Generamos un par clave privada RSA / clave pública CSR:
# openssl req -newkey rsa:2048 -nodes -keyout domain.com.key -out domain.com.csr -config ./openssl.cnf # openssl req -noout -text -in domain.com.csr
- Generamos el certificado auto-firmado:
# openssl x509 -req -days 365 -in domain.com.csr -signkey domain.com.key -out domain.com.crt -extensions v3_req -extfile ./openssl.cnf # openssl x509 -noout -text -in domain.com.crt
- Creamos el certificado en formato PEM:
# cat domain.com.key domain.com.crt > domain.com.pem
- Por último, configuramos el servidor web y reiniciamos el daemon para activar los cambios.
3 Comentarios en “HTTP seguro: SSL bajo Linux”
Deja un comentario
Muy útil
gracias
para traducir esto como lo puedo acer?
48dabef3e3fe0c51c6eeae3d393ca173
Muy buen post. Me ha sido muy útil. Gracias