--- title: "Cifrado de datos en tránsito con TLS en Ubuntu 24.04" description: "Protege los datos en tránsito en Ubuntu 24.04: HTTPS en Apache con Let's Encrypt, solo TLS 1.2 y 1.3, HSTS y conexiones remotas a MySQL siempre cifradas." category: "Cumplimiento de las normas" language: es source: https://cubepath.com/es/docs/cumplimiento-de-las-normas/cifrado-de-datos-en-transito updated: 2026-09-25 --- # Cifrado de datos en tránsito con TLS en Ubuntu 24.04 Cifrar los datos en tránsito significa que nada de lo que viaja por la red entre tus usuarios, tu servidor y otros servicios pueda leerse o modificarse por el camino. Es un requisito explícito de normas como el RGPD (artículo 32) o PCI-DSS, y en la práctica se consigue con TLS. En este tutorial configurarás HTTPS en Apache con un certificado de Let's Encrypt en Ubuntu 24.04, limitarás el servidor a TLS 1.2 y 1.3, activarás HSTS y obligarás a que las conexiones remotas a MySQL vayan cifradas. ## Requisitos previos - Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath. - Un usuario no root con privilegios `sudo`. - Un dominio con registros DNS `A` (y `AAAA` si usas IPv6) apuntando a la IP del servidor, tanto para `your_domain` como para `www.your_domain`. Sustituye **`your_domain`** por tu dominio en todos los comandos. - Los puertos 80 y 443 accesibles desde Internet. Let's Encrypt valida el dominio por el puerto 80. - Opcional: MySQL 8 instalado, si quieres seguir el paso 6. ## Paso 1: Instalar Apache y abrir el cortafuegos Instala Apache: ```bash sudo apt update sudo apt install apache2 ``` Si usas UFW, permite SSH antes de nada para no perder el acceso, y después HTTP y HTTPS con el perfil `Apache Full`: ```bash sudo ufw allow OpenSSH sudo ufw allow 'Apache Full' sudo ufw enable ``` Comprueba las reglas: ```bash sudo ufw status ``` ```output Status: active To Action From -- ------ ---- OpenSSH ALLOW Anywhere Apache Full ALLOW Anywhere OpenSSH (v6) ALLOW Anywhere (v6) Apache Full (v6) ALLOW Anywhere (v6) ``` ## Paso 2: Crear el virtual host del dominio Certbot necesita un virtual host con `ServerName` para saber dónde instalar el certificado. Crea el directorio del sitio y una página de prueba: ```bash sudo mkdir -p /var/www/your_domain echo '

your_domain funciona

' | sudo tee /var/www/your_domain/index.html ``` Crea el archivo del virtual host: ```bash sudo nano /etc/apache2/sites-available/your_domain.conf ``` ```apache ServerName your_domain ServerAlias www.your_domain DocumentRoot /var/www/your_domain ErrorLog ${APACHE_LOG_DIR}/your_domain_error.log CustomLog ${APACHE_LOG_DIR}/your_domain_access.log combined ``` Activa el sitio, desactiva el sitio por defecto y valida la sintaxis antes de recargar: ```bash sudo a2ensite your_domain.conf sudo a2dissite 000-default.conf sudo apache2ctl configtest ``` ```output Syntax OK ``` ```bash sudo systemctl reload apache2 ``` Comprueba que el sitio responde por HTTP: ```bash curl -s http://your_domain ``` ```output

your_domain funciona

``` ## Paso 3: Obtener un certificado de Let's Encrypt Instala Certbot y su plugin para Apache: ```bash sudo apt install certbot python3-certbot-apache ``` Solicita el certificado para los dos nombres. El plugin de Apache activa `mod_ssl`, crea el virtual host HTTPS en `/etc/apache2/sites-available/your_domain-le-ssl.conf` y, con `--redirect`, añade la redirección permanente de HTTP a HTTPS: ```bash sudo certbot --apache --redirect -d your_domain -d www.your_domain ``` La primera vez te pedirá un correo para avisos de caducidad y que aceptes los términos del servicio. Al terminar verás algo similar a: ```output Successfully received certificate. Certificate is saved at: /etc/letsencrypt/live/your_domain/fullchain.pem Key is saved at: /etc/letsencrypt/live/your_domain/privkey.pem ... Congratulations! You have successfully enabled HTTPS on https://your_domain and https://www.your_domain ``` Comprueba que HTTP redirige a HTTPS: ```bash curl -sI http://your_domain | grep -iE '^(HTTP|Location)' ``` ```output HTTP/1.1 301 Moved Permanently Location: https://your_domain/ ``` ## Paso 4: Limitar el servidor a TLS 1.2 y TLS 1.3 TLS 1.0 y 1.1 están obsoletos (RFC 8996) y no se admiten en PCI-DSS. El virtual host HTTPS que genera Certbot incluye el archivo `/etc/letsencrypt/options-ssl-apache.conf`, que ya los desactiva. Compruébalo: ```bash grep -E '^SSL(Protocol|HonorCipherOrder|SessionTickets)' /etc/letsencrypt/options-ssl-apache.conf ``` ```output SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1 SSLHonorCipherOrder off SSLSessionTickets off ``` `all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1` deja solo TLS 1.2 y 1.3. No edites este archivo: Certbot lo gestiona y lo actualiza con recomendaciones nuevas. Si algún día necesitas otra política, define `SSLProtocol` en tu propio virtual host después de la línea `Include`. Verifica desde el propio servidor que se negocia TLS 1.3: ```bash openssl s_client -connect your_domain:443 -servername your_domain /dev/null | grep '^New,' ``` ```output New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384 ``` Para una comprobación completa de versiones y cifrados aceptados, ejecuta desde otra máquina con `nmap` instalado: ```bash nmap --script ssl-enum-ciphers -p 443 your_domain ``` La salida solo debe listar `TLSv1.2` y `TLSv1.3`. También puedes analizar el dominio con SSL Labs (ssllabs.com/ssltest). ## Paso 5: Activar HSTS La redirección a HTTPS deja una ventana: la primera petición de un navegador sigue saliendo por HTTP en claro. La cabecera HTTP Strict Transport Security (HSTS) indica al navegador que use siempre HTTPS con ese dominio durante el tiempo que marques. Activa el módulo `headers`: ```bash sudo a2enmod headers ``` Edita el virtual host HTTPS generado por Certbot: ```bash sudo nano /etc/apache2/sites-available/your_domain-le-ssl.conf ``` Añade la línea `Header` dentro del bloque ``, por ejemplo después de `DocumentRoot`: ```apache ServerName your_domain ServerAlias www.your_domain DocumentRoot /var/www/your_domain Header always set Strict-Transport-Security "max-age=300" ErrorLog ${APACHE_LOG_DIR}/your_domain_error.log CustomLog ${APACHE_LOG_DIR}/your_domain_access.log combined SSLCertificateFile /etc/letsencrypt/live/your_domain/fullchain.pem SSLCertificateKeyFile /etc/letsencrypt/live/your_domain/privkey.pem Include /etc/letsencrypt/options-ssl-apache.conf ``` Valida y recarga: ```bash sudo apache2ctl configtest sudo systemctl reload apache2 ``` Comprueba la cabecera: ```bash curl -sI https://your_domain | grep -i strict-transport ``` ```output strict-transport-security: max-age=300 ``` > Importante: empieza con un `max-age` corto (300 segundos) mientras compruebas que todo el sitio y sus subdominios funcionan por HTTPS. Cuando estés seguro, súbelo a un año o más (`max-age=31536000`). Los navegadores recuerdan la cabecera durante ese tiempo, así que un valor largo es difícil de deshacer. ## Paso 6: Obligar a MySQL a usar conexiones cifradas HTTPS protege el tráfico de los usuarios, pero si tu aplicación o tus réplicas se conectan a MySQL a través de la red, esas consultas también viajan con datos personales. MySQL 8 en Ubuntu genera certificados TLS automáticamente al instalarse y acepta TLS por defecto, pero también permite conexiones sin cifrar. Si MySQL solo escucha en `127.0.0.1`, el tráfico no sale del servidor y puedes saltarte este paso. Edita la configuración del servidor: ```bash sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf ``` Añade esta línea dentro de la sección `[mysqld]`: ```ini require_secure_transport = ON ``` Reinicia MySQL y comprueba la variable: ```bash sudo systemctl restart mysql sudo mysql -e "SHOW VARIABLES LIKE 'require_secure_transport';" ``` ```output +--------------------------+-------+ | Variable_name | Value | +--------------------------+-------+ | require_secure_transport | ON | +--------------------------+-------+ ``` Las conexiones locales por socket Unix se consideran seguras, así que `sudo mysql` sigue funcionando. Desde un cliente remoto, una conexión sin TLS se rechaza: ```bash mysql -h your_server_ip -u app_user -p --ssl-mode=DISABLED ``` ```output ERROR 3159 (HY000): Connections using insecure transport are prohibited while --require_secure_transport=ON. ``` Y una conexión normal negocia TLS. Compruébalo con el comando `status` del cliente: ```bash mysql -h your_server_ip -u app_user -p -e 'status' | grep -i ssl ``` ```output SSL: Cipher in use is TLS_AES_256_GCM_SHA384 ``` Los certificados autogenerados cifran la conexión pero no permiten al cliente verificar la identidad del servidor. Si la conexión cruza redes que no controlas, configura en el cliente `--ssl-mode=VERIFY_IDENTITY` con un certificado firmado por tu propia CA, o lleva el tráfico por una red privada o una VPN. ## Paso 7: Verificar la renovación automática Los certificados de Let's Encrypt caducan a los 90 días o antes. El paquete `certbot` instala un temporizador de systemd que intenta renovarlos dos veces al día. Comprueba que está activo: ```bash systemctl list-timers certbot.timer ``` ```output NEXT LEFT LAST PASSED UNIT ACTIVATES Fri 2026-09-25 21:14:00 UTC 9h left Fri 2026-09-25 09:02:11 UTC 2h ago certbot.timer certbot.service ``` Simula una renovación para asegurarte de que funcionará cuando toque: ```bash sudo certbot renew --dry-run ``` ```output Congratulations, all simulated renewals succeeded: /etc/letsencrypt/live/your_domain/fullchain.pem (success) ``` ## Solución de problemas **Certbot falla con `Timeout during connect` o `Connection refused`.** Let's Encrypt no llega al puerto 80. Revisa que el DNS apunta a la IP correcta (`dig +short your_domain`) y que UFW permite `Apache Full`. **`Invalid command 'Header'` al validar la configuración.** El módulo `headers` no está activo. Ejecuta `sudo a2enmod headers` y recarga Apache. **Avisos de contenido mixto en el navegador.** La página carga recursos con URL `http://`. Cambia esas URL a `https://` en la aplicación o en la base de datos (en WordPress, la URL del sitio). **Un cliente antiguo no conecta tras desactivar TLS 1.0/1.1.** Es el comportamiento esperado: ese cliente debe actualizarse. No vuelvas a activar versiones obsoletas para un caso aislado. ## Conclusión Tu servidor sirve el sitio solo por HTTPS con TLS 1.2 y 1.3, los navegadores recuerdan usar HTTPS gracias a HSTS, los certificados se renuevan solos y MySQL rechaza conexiones remotas sin cifrar. Como siguientes pasos, aplica el mismo criterio al resto de servicios que escuchan en la red (correo con STARTTLS, Redis, paneles internos), sube el `max-age` de HSTS cuando hayas validado el sitio y complementa esta configuración con el cifrado de los datos en reposo.