// Engineering Log

HTTP, HTTPS y TLS: Parte 4 — ¿Qué es HTTPS y cómo funciona el apretón de manos TLS?

Publicado el 02.10.2026

// Ruta rapida

Este articulo pertenece al tema Seguridad y proteccion.

HTTPS — es HTTP transmitido dentro de una conexión TLS (Transport Layer Security). TLS resuelve tres tareas: cifra los datos para que no puedan leerse en tránsito; verifica su integridad para que no sean modificados; y confirma que realmente está hablando con el servidor example.ru, y no con alguien que se ha interpuesto en medio. La tercera tarea se resuelve con un certificado, y sin ella las dos primeras carecen de sentido: un canal cifrado hacia un atacante no protege nada.

SSL — nombre antiguo del protocolo. La última versión SSL 3.0 está prohibida desde 2015 (RFC 7568), pero la palabra se quedó: «certificado SSL», ssl_certificate en Nginx, la biblioteca OpenSSL. Hoy en día cuando se dice SSL normalmente se entiende TLS.

Qué se cifra y qué se ve

Dentro de TLS se transmite todo el HTTP: método, ruta, query string, cabeceras, cookies, cuerpo. Un observador en la red — proveedor, propietario del Wi-Fi, equipo DPI — ve:

  • direcciones IP y puertos del cliente y del servidor;
  • el nombre del sitio en el campo SNI del mensaje inicial del handshake (si no se usa ECH);
  • la consulta DNS para ese nombre, si el DNS no está cifrado;
  • el volumen y el tiempo de transferencia de datos;
  • parámetros del handshake con los que se puede identificar el tipo de cliente (más detalles — HTTP, HTTPS y TLS: Parte 8 — ¿Qué son JA3 y JA4? Huellas del cliente TLS).

La ruta /account/orders?id=42 y el contenido de la página no se ven. Por eso los bloqueos en la red funcionan por dirección IP, por SNI o por DNS, pero no por páginas individuales.

Certificado

Un certificado X.509 es la clave pública del servidor junto con datos sobre a quién pertenece, firmado por una autoridad de certificación (CA). Campos principales:

  • Subject Alternative Name (SAN) — lista de nombres para los que vale el certificado: example.ru, www.example.ru, *.example.ru. Los navegadores comprueban solo SAN; el campo Common Name no se usa para la verificación de nombre desde 2017;
  • periodo de validez — notBefore y notAfter;
  • emisor — quién lo firmó;
  • clave pública — RSA (normalmente 2048 bits) o ECDSA (P-256).

El asterisco en SAN cubre un nivel: *.example.ru vale para shop.example.ru, pero no para example.ru ni para a.shop.example.ru.

Los plazos de validez se acortan. El consorcio industrial CA/Browser Forum en 2025 aprobó una reducción escalonada del periodo máximo de los certificados públicos: 200 días desde el 15 de marzo de 2026, 100 días desde marzo de 2027 y 47 días desde marzo de 2029. Los certificados de Let’s Encrypt viven 90 días, y el proyecto se mueve hacia plazos aún más cortos. La conclusión es una sola: la emisión y renovación deben ser automáticas (cliente ACME certbot, acme.sh, ACME integrado en Caddy y Traefik).

Cadena de confianza

El certificado del sitio no está firmado por el certificado raíz de la CA, sino por uno intermedio. La cadena se ve así:

example.ru            ← certificado del sitio (leaf), firmado por un intermedio
  └ R11 (Let's Encrypt) ← intermedio, firmado por el raíz
      └ ISRG Root X1    ← raíz, está en el almacén de confianza del SO o del navegador

El servidor está obligado a enviar el certificado del sitio y todos los intermedios. No hace falta enviar el raíz — ya lo tiene el cliente. El error de configuración más frecuente es que el servidor entregue solo el certificado del sitio sin el intermedio. Los navegadores en ordenadores a menudo lo perdonan (Chrome y Firefox saben encontrar o cachear intermedios), pero curl, Python, Java y las aplicaciones móviles no, y devuelven error de verificación. Por eso en certbot para Nginx se indica fullchain.pem, no cert.pem.

El cliente comprueba:

  1. cada firma de la cadena es válida y la cadena termina en una raíz del almacén de confianza;
  2. los certificados no están caducados;
  3. el nombre del sitio está en SAN;
  4. el certificado no está revocado (los mecanismos de revocación difieren entre navegadores; Let’s Encrypt en 2025 dejó de soportar OCSP y publica solo listas CRL).

Handshake TLS 1.3

TLS 1.3 (RFC 8446, 2018) establece una conexión protegida en un solo ida y vuelta (1-RTT) sobre una conexión TCP ya abierta.

1. ClientHello (cliente → servidor, en texto claro):

  • versiones soportadas (extensión supported_versions);
  • lista de conjuntos de cifrado: TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256;
  • grupos soportados para el intercambio de claves: X25519MLKEM768, x25519, secp256r1;
  • key_share — ya la parte pública lista para una o dos grupos. El cliente adivina cuál elegirá el servidor y con eso ahorra un ida y vuelta;
  • SNI — el nombre del sitio para que el servidor elija el certificado correcto;
  • ALPN — qué protocolos el cliente está dispuesto a usar sobre TLS: h2, http/1.1;
  • número aleatorio y otras extensiones.

2. ServerHello (servidor → cliente): conjunto de cifrado elegido y su parte de la clave. A partir de ese momento ambas partes tienen un secreto común (algoritmo de intercambio Diffie–Hellman) y todo lo siguiente se cifra.

3. Parte cifrada desde el servidor:

  • EncryptedExtensions — protocolo ALPN elegido y otros parámetros;
  • Certificate — cadena de certificados;
  • CertificateVerify — firma de toda la negociación con la clave privada del certificado. Así el servidor demuestra que posee la clave, y no que solo reenviaba un certificado ajeno;
  • Finished — suma de verificación del handshake.

4. Finished desde el cliente — y de inmediato la primera petición HTTP en el mismo paquete.

En TLS 1.3 incluso el certificado del servidor se transmite cifrado, y un observador pasivo no ve qué certificado devolvió el servidor. Quedan en claro ClientHello y ServerHello.

Si el cliente adivinó mal el grupo en key_share, el servidor responde HelloRetryRequest y el handshake ocupa dos idas y vueltas.

Reanudación de sesión y 0-RTT. Tras el handshake el servidor da al cliente un ticket de sesión (session ticket). En la siguiente conexión el cliente lo presenta y se ahorra la verificación del certificado, y en modo 0-RTT envía la primera petición junto con el ClientHello. Los datos 0-RTT se pueden interceptar y retransmitir, por eso solo deben usarse para peticiones seguras como GET; en Nginx 0-RTT está desactivado por defecto (ssl_early_data off).

Handshake TLS 1.2

TLS 1.2 (RFC 5246, 2008) aún está muy soportado y requiere dos idas y vueltas:

  1. ClientHello — versiones y conjuntos de cifrado;
  2. ServerHello, Certificate (en texto claro), ServerKeyExchange, ServerHelloDone;
  3. ClientKeyExchange, ChangeCipherSpec, Finished desde el cliente;
  4. ChangeCipherSpec y Finished desde el servidor — solo entonces se puede enviar HTTP.

Diferencias con 1.3 importantes en la práctica:

  • una ida y vuelta más lento: con una latencia a servidor de 100 ms son 100 ms añadidos a cada conexión nueva;
  • el certificado del servidor se ve en la red;
  • permite algoritmos obsoletos: intercambio RSA sin forward secrecy, cifrados CBC, SHA-1. Hay que deshabilitarlos en la configuración del servidor (más detalles — HTTP, HTTPS y TLS: Parte 5 — Versiones de TLS, conjuntos de cifrado, SNI, ALPN, ECH y mTLS);
  • con intercambio de claves RSA, quien obtenga después la clave privada del servidor podrá descifrar todas las sesiones grabadas antes. En TLS 1.3 ese intercambio se suprimió: las claves de sesión son efímeras y la filtración de la clave del certificado no revela tráfico pasado. Esta propiedad se llama secreto hacia adelante (forward secrecy).

Dónde termina TLS

TLS protege el tramo entre el cliente y quien posee el certificado. Si delante del sitio hay un CDN o un proxy inverso, el TLS desde el navegador termina en él (terminación TLS), y a partir de ahí hasta su servidor la petición circula por otra conexión. Ese segundo tramo también debe cifrarse si pasa por una red de terceros: en Cloudflare, por ejemplo, el modo Flexible envía peticiones al servidor por HTTP normal, y entre el CDN y el servidor los datos van en claro. Se necesita el modo Full (strict) con verificación del certificado en el servidor.

Vea el apretón de manos usted mismo

Pasos del apretón de manos. La opción -state de openssl s_client imprime cada mensaje que el cliente envió o recibió. Salida para TLS 1.3 (sitio detrás de CloudFront, septiembre de 2026):

bash
echo | openssl s_client -connect example.ru:443 -servername example.ru -state 2>&1 | grep -E '^SSL_connect|^Negotiated|^New'
SSL_connect:SSLv3/TLS write client hello
SSL_connect:SSLv3/TLS read server hello
SSL_connect:TLSv1.3 read encrypted extensions
SSL_connect:SSLv3/TLS read server certificate
SSL_connect:TLSv1.3 read server certificate verify
SSL_connect:SSLv3/TLS read finished
SSL_connect:SSLv3/TLS write change cipher spec
SSL_connect:SSLv3/TLS write finished
Negotiated TLS1.3 group: X25519MLKEM768
New, TLSv1.3, Cipher is TLS_AES_128_GCM_SHA256

Todo lo que aparece después de read server hello, el cliente lo recibió ya cifrado. change cipher spec en TLS 1.3 es un mensaje vacío para compatibilidad con equipos de red antiguos. El grupo X25519MLKEM768 es un intercambio de claves híbrido post-cuántico (más detalles — en el artículo HTTP, HTTPS y TLS: Parte 5 — Versiones de TLS, conjuntos de cifrado, SNI, ALPN, ECH y mTLS).

El mismo servidor con la opción -tls1_2:

SSL_connect:SSLv3/TLS write client hello
SSL_connect:SSLv3/TLS read server hello
SSL_connect:SSLv3/TLS read server certificate
SSL_connect:SSLv3/TLS read server key exchange
SSL_connect:SSLv3/TLS read server done
SSL_connect:SSLv3/TLS write client key exchange
SSL_connect:SSLv3/TLS write change cipher spec
SSL_connect:SSLv3/TLS write finished
SSL_connect:SSLv3/TLS read server session ticket
SSL_connect:SSLv3/TLS read change cipher spec
SSL_connect:SSLv3/TLS read finished

Aquí se ven ambas idas y vueltas: certificado y clave del servidor llegan en texto claro, el cliente responde con su clave y espera el finished del servidor. Para un análisis completo de los mensajes use la opción -msg, y en compilaciones de OpenSSL con soporte de trazas — -trace.

Cuánto cuesta una ida y vuelta extra. Medición con curl del mismo sitio, cinco peticiones por versión, promedio:

bash
curl --tlsv1.2 --tls-max 1.2 -s -o /dev/null -w '%{time_connect} %{time_appconnect}\n' https://example.ru/
curl --tlsv1.3               -s -o /dev/null -w '%{time_connect} %{time_appconnect}\n' https://example.ru/
VersiónTCP (≈ 1 RTT)Apretón de manos TLS
TLS 1.215 ms37 ms
TLS 1.317 ms23 ms

El handshake TLS 1.2 tomó aproximadamente dos RTT, TLS 1.3 algo más de uno (más tiempo de cómputo). En un servidor en otro hemisferio, donde el RTT es 150–200 ms, la diferencia entre versiones ya son 150–200 ms en cada conexión nueva.

En Wireshark. El filtro tls.handshake dejará solo los mensajes del apretón de manos. En TLS 1.2 en el paquete Certificate se puede desplegar el certificado del servidor; en TLS 1.3 tras el ServerHello solo se ven registros Application Data — el certificado está cifrado.

En el navegador. Clic en el icono a la izquierda de la dirección → «Conexión segura» → «El certificado es válido» mostrará la cadena, y en Chrome en la pestaña Security de DevTools — la versión TLS, el grupo de intercambio de claves y el cifrado.

Certificados autofirmados y corporativos

Un certificado autofirmado cifra igualmente de forma fiable, pero el cliente no tiene manera de verificar que sea legítimo, y el navegador muestra una advertencia. Para servicios internos, en lugar de certificados autofirmados es mejor crear una CA interna propia (por ejemplo, step-ca) e instalar su certificado raíz en los puestos de trabajo.

Los sistemas corporativos de protección y los antivirus con frecuencia descifran HTTPS: instalan en el equipo su certificado raíz y emiten sobre la marcha certificados falsos para cada sitio. En el navegador eso se ve por el emisor del certificado — en lugar de Let’s Encrypt u otra CA pública aparece el nombre del antivirus o de la empresa. Las aplicaciones con una lista de certificados de confianza integrada (pinning) dejan de funcionar en esa red.

Errores típicos

  • En Nginx se indicó cert.pem en lugar de fullchain.pem — el navegador funciona, pero APIs en Python y aplicaciones móviles fallan con error de verificación de certificado.
  • El certificado se emitió para example.ru, pero los usuarios acceden a www.example.ru — error de nombre no coincidente.
  • La renovación está configurada, pero el servidor web no recarga el certificado tras ella — el sitio sigue sirviendo el certificado antiguo ya caducado.
  • Cifrado solo hasta el CDN, y del CDN al servidor — HTTP en claro.
  • Para comprobar «si funciona» se usa curl -k en scripts de monitorización — la verificación del certificado está desactivada y nadie nota la expiración.

// Tarea parecida

Si estas resolviendo algo parecido

Este articulo pertenece a uno de los temas principales de trabajo. Puedes seguir leyendo sobre el tema, ir a la pagina principal para entender a que me dedico o abrir directamente los servicios.

Tema del articulo

Seguridad y proteccion

SSL, hardening, accesos, proteccion de servicios y configuraciones seguras.

Tareas frecuentes de esta tema

  • Configurar SSL, certificados y conexiones seguras
  • Restringir accesos y cerrar puntos de entrada innecesarios
  • Reforzar la configuracion del servidor y los servicios

// Siguiente paso

Si necesitas ayuda con este tema y no solo otro articulo, es mejor ir directo a la pagina del servicio. La pagina principal y la seleccion de materiales quedan como rutas secundarias.

Abrir servicios

// Contact

¿Necesitas ayuda?

Escríbeme y te ayudaré a resolver el problema

Respondo en un día laborable (03:00-13:00 GMT)

Или оставьте заявку здесь:

Confirme que no es un bot.

Escribir y recibir una respuesta rápida