HTTPS (HTTP seguro): SSL

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.

  1. Dominio

    Necesitamos un dominio registrado, por ejemplo domain.com

  2. 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
  3. Certificado SSL

    Ahora necesitamos obtener el certificado SSL (domain.com.crt) para lo cual debe firmarlo una CA (Certification Authority, Autoridad Certificadora).

    1. 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).

    2. 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.
    3. 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
  4. 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
  5. 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):

    1. domain.com.key: clave privada RSA.
    2. domain.com.csr: solicitud de certificado, contiene la clave pública.
    3. domain.com.crt: certificado SSL de nuestro dominio.
    4. domain.com.pem: clave privada RSA y certificado x509 concatenados.
    5. 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.

  6. Servidor web
    1. 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:
    2. 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
  7. 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

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.

Artículos en la categoría "Encriptación y Criptografía"

  1. Checksum MD5
  2. Conexión remota segura SSH
  3. Software de encriptación GPG
  4. APT seguro (Secure APT)
  5. HTTPS (HTTP seguro): SSL

9 Comments:

  1. Muy útil
    gracias

  2. para traducir esto como lo puedo acer?
    48dabef3e3fe0c51c6eeae3d393ca173

  3. Muy buen post. Me ha sido muy útil. Gracias

  4. Muy buen Post, felicidades!!!

  5. El problema de los certificados autofirmados es que los navegadores lo rechazan, ¿hay alguna manera de hacer que no pase esto?

  6. Los certificados autofirmados no están firmados por una CA (entidad con autoridad certificadora -Ceres, Verisign, etc.-): cualquier navegador que accede a un servidor HTTPS que tiene un certificado autofirmado, avisa al usuario de su falta de autentificación. El único modo de que el usuario no vea este aviso, es que tenga instalado nuestro certificado y, para ello deberemos hacer que lo instale; por tanto, la primera vez que entre en un dominio HTTPS autofirmado ( y siempre que entre y no tenga instalado el certificado ), el navegador le avisa y le presenta también la opción de que admita dicho certificado por una vez o siempre (instalar certificado), que no está firmado por una CA sino por nosotros. Es en éste momento en el que el usuario debe admitir la excepción y aceptar la instalación de dicho certificado para que no se le muestre el mensaje de aviso.

  7. Muchas gracias por tus apuntes, han sido fundamentales en mi búsqueda.

  8. Es dificil dаr con contenidoѕ bien expuestos, dе fоrma que aprovecho para reconocerselo al autor. Sigue así

  9. guerrilla-marketing

    Mi felicitación, muy grande el post. Hasta otra.