El protocolo HTTPS

En este capítulo se abordan temas referentes al uso del protocolo HTTPS, el cual no es sino el mismo HTTP con una S demás (?). Esta se refiere a Secure, o sea es la versión segura del HTTP, la cual se encuentra bajo un túnel cifrado mediante el protocolo TLS(antes SSL, actualmente deprecado), que por convención suele viajar sobre el puerto 443 (no es raro encontrar aplicaciones utilizando el puerto 8443, dado que esto es una convención, el protocolo puede funcionar sobre cualquier puerto). Formalmente se define en el RFC 2818.

En este sentido, la importancia radica en que al utilizar una red pública o compartida, el tráfico HTTP viaja en texto plano, lo que lo vuelve susceptible a problemas de confidencialidad así como de integridad de los datos, ya que un atacante puede ver y manipular tanto las peticiones como el contenido que recibe la víctima, suponiendo un evidente riesgo.

Ese túnel ofrece tres garantías y se mencionan a continuación:

  • Confidencialidad: Ningún tercero en el camino puede leer el contenido.
  • Integridad: Nadie puede modificar el contenido sin pasar desapercibido.
  • Autenticación: El servidor es quien dice ser mediante una autoridad de confianza que valida la identidad del sitio.

El handshake TLS

El handshake consiste en una negociación respecto a la comunicación que se establece entre el cliente y el servidor. Hoy en día, las dos versiones vigentes son el TLS 1.2 y 1.3.

TLS 1.2

Esta versión posee diferencias sustanciales con su sucesor, particularmente el handshake realiza dos vueltas completas (2-RTT) y una parte viaja en claro. A continuación se describen los pasos del handshake para establecer el túnel cifrado:

  • El cliente manda ClientHello: versiones que soporta, lista de cipher suites y el SNI (Server Name Indication), que dice a qué dominio se conecta (necesario cuando una IP aloja varios sitios).
  • El servidor responde ServerHello con la suite elegida y envía su certificado en texto claro.
  • Se acuerdan las claves y, a partir del mensaje Finished, todo va cifrado.

TLS 1.3

Para esta versión, el diseño se rehízo desde cero (RFC 8446, agosto de 2018). Todo lo que sigue al ServerHello va cifrado, incluido el certificado. Además elimina de raíz un montón de mecanismos que solo servían para tener accidentes: el intercambio de claves RSA y Diffie-Hellman (DH) estático, los modos CBC, RC4, la compresión, la renegociación y los grupos DH a medida. Lo que queda es un conjunto pequeño y sano.

tls-handshake

Hay una optimización de TLS 1.3 que conviene conocer porque tiene truco: 0-RTT o early data, que deja al cliente mandar datos en el primer paquete al reconectar. El propio RFC 8446 advierte que esos datos no tienen forward secrecy y no hay garantía de no-replay entre conexiones: un atacante puede reenviar ese primer paquete. No es un fallo de TLS, es una compensación velocidad/seguridad que la aplicación debe entender antes de activarla.

Para quien audita, saber qué parte del handshake va en claro es directamente saber qué ve un observador pasivo. En TLS 1.2 el certificado y el SNI viajan sin cifrar; en TLS 1.3 solo el SNI (salvo que se use Encrypted Client Hello). Y saber que el ClientHello anuncia las versiones soportadas es entender dónde ocurre un ataque de downgrade: si el atacante puede forzar que se elija una versión vieja, todo lo demás da igual.

Los algoritmos

El nombre del cipher suite (en TLS 1.2) se lee de acuerdo al siguiente esquema:

TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
     │     │        │           │
     │     │        │           └─ hash del PRF: SHA-256
     │     │        └───────────── cifrado simétrico: AES-128 en modo GCM (AEAD)
     │     └────────────────────── autenticación del servidor: firma RSA
     └──────────────────────────── intercambio de claves: ECDHE (efímero → forward secrecy)

En TLS 1.3, se acortaron los nombres porque el intercambio de claves ya no forma parte de la suite: TLS_AES_256_GCM_SHA384 describe solo el cifrado y el hash. Cada pieza es una decisión de seguridad:

  • Intercambio de claves: cómo cliente y servidor acuerdan la clave simétrica sin que un tercero la deduzca. El RSA estático (la clave se cifra con la llave pública del servidor) tiene un problema fatal: quien capture el tráfico hoy y robe la clave privada mañana, descifra todo el histórico. El efímero —ECDHE, DHE— genera una clave de un solo uso por sesión, así que robar la clave privada del servidor no descifra sesiones pasadas. A esa propiedad se le llama forward secrecy, y TLS 1.3 la hace obligatoria.
  • Autenticación/firma: con qué firma el servidor para probar su identidad. RSA, ECDSA (curvas elípticas, claves más cortas para la misma fuerza) o EdDSA.
  • Cifrado simétrico: el que cifra el contenido de verdad. Hoy solo debería verse AEAD (Authenticated Encryption with Associated Data): AES-GCM o ChaCha20-Poly1305. AEAD cifra y autentica en una sola operación; los viejos modos CBC cifraban por un lado y autenticaban por otro (MAC-then-encrypt), y de ahí es justo donde viven las vulnerabilidades BEAST y Lucky13.
  • Hash: SHA-256 o superior para las funciones de derivación. MD5 y SHA-1 quedaron fuera.

El certificado X.509

El certificado es el documento que el servidor presenta para probar su identidad. Su formato es X.509, definido en el RFC 5280, y por dentro es una estructura de campos firmada por la autoridad emisora. Puntualmente nos interesan los siguientes elementos:

  • Subject: a quién identifica el certificado (el dominio).
  • Issuer: quién lo firmó (la CA).
  • Validity: las fechas notBefore y notAfter. Fuera de esa ventana, el navegador lo rechaza.
  • SubjectPublicKeyInfo: la clave pública y su algoritmo.
  • Subject Alternative Name (SAN): la extensión que lista los nombres que el certificado cubre.

Aquí vive una confusión que sigue causando incidentes. El viejo RFC 2818 decía que la identidad se valida contra el SAN y, si no hay, se usa como fallback el Common Name (CN) del subject. Esa parte quedó obsoleta: los navegadores modernos exigen el SAN e ignoran el CN. Un certificado con el dominio solo en el CN y no en el SAN funciona en openssl pero falla en Chrome y Firefox. Es un fallo real y frecuente, y es exactamente el tipo de discrepancia entre "lo que dice la especificación vieja" y "lo que hace el navegador de hoy" donde aparecen los bugs.

Un detalle de los comodines: un * cubre un solo componente del nombre. *.site.com cubre a.site.com pero no a.b.site.com ni el dominio raíz pelado. Un comodín demasiado amplio es un riesgo: una clave comprometida afecta a todo lo que cubre.

Puedes inspeccionar cualquier certificado con openssl:

openssl x509 -in cert.pem -noout -text
openssl x509 -in cert.pem -noout -subject -issuer -dates -ext subjectAltName

CA — Certificate Authority

Una CA (Certificate Authority) es un tercero en el que tu sistema ya confía de antemano, y esa confianza viene preinstalada: el sistema operativo y el navegador traen un almacén de confianza (trust store) con las claves públicas de las CA raíz reconocidas.

La confianza no es directa, es una cadena. Las CA no firman los certificados de los sitios con su raíz, sino con certificados intermedios. La cadena va de la hoja a la raíz, y cada eslabón lo firma el siguiente:

cadena-certificados

Las versiones de SSL y TLS

El protocolo tiene treinta años de historia, y esa historia se lee como una cadena de fallos: cada versión nueva es, en buena parte, la respuesta a un ataque contra la anterior. Saber en qué año murió cada una y por qué es lo que te deja juzgar una configuración de un vistazo.

┌─────────┬──────┬─────────────┬─────────────────────────────────────────────────┐
│ Versión │ Año  │ Estado hoy  │ Nota                                            │
├─────────┼──────┼─────────────┼─────────────────────────────────────────────────┤
│ SSL 2.0 │ 1995 │ Prohibido   │ Roto de raíz. Habilita DROWN.                   │
│ SSL 3.0 │ 1996 │ Obsoleto    │ Deprecado por RFC 7568 (2015). Cayó con POODLE. │
│ TLS 1.0 │ 1999 │ Obsoleto    │ Prohibido por RFC 8996 (2021). BEAST.           │
│ TLS 1.1 │ 2006 │ Obsoleto    │ Prohibido por RFC 8996 (2021).                  │
│ TLS 1.2 │ 2008 │ Vigente     │ RFC 5246. Válido si se configura bien.          │
│ TLS 1.3 │ 2018 │ Recomendado │ RFC 8446. Forward secrecy siempre.              │
└─────────┴──────┴─────────────┴─────────────────────────────────────────────────┘

Ataques al canal TLS

La lista de ataques con nombre propio se ordena en pocas familias según el mecanismo.

Esta tabla es el mapa. Cada fila la desarrollamos abajo, pero léela como referencia rápida: cuando una herramienta te marque uno de estos nombres, aquí está qué lo habilita y qué lo cierra.

┌───────────────┬──────────────────────────┬───────────────┬───────────────────────────┐
│ Ataque        │ Lo habilita              │ CVE           │ Mitigación                │
├───────────────┼──────────────────────────┼───────────────┼───────────────────────────┤
│ POODLE        │ SSLv3 + CBC              │ CVE-2014-3566 │ Quitar SSLv3              │
│ BEAST         │ TLS 1.0 + CBC            │ CVE-2011-3389 │ TLS 1.2+ / AEAD           │
│ Lucky13       │ CBC (MAC-then-encrypt)   │ CVE-2013-0169 │ AEAD, sin CBC             │
│ CRIME         │ Compresión TLS           │ CVE-2012-4929 │ Desactivar compresión TLS │
│ BREACH        │ Compresión HTTP (gzip)   │ CVE-2013-3587 │ No reflejar secretos      │
│ Sweet32       │ 3DES/DES (bloque 64-bit) │ CVE-2016-2183 │ Quitar 3DES y DES         │
│ RC4           │ Sesgos del keystream     │ CVE-2015-2808 │ Quitar RC4 (RFC 7465)     │
│ FREAK         │ RSA_EXPORT (512-bit)     │ CVE-2015-0204 │ Quitar suites EXPORT      │
│ Logjam        │ DHE_EXPORT (512-bit)     │ CVE-2015-4000 │ Quitar EXPORT, DH >= 2048 │
│ DROWN         │ SSLv2 activo             │ CVE-2016-0800 │ Erradicar SSLv2           │
│ ROBOT         │ Intercambio RSA          │ por vendor    │ (EC)DHE / TLS 1.3         │
│ Renegociación │ Renegociación insegura   │ CVE-2009-3555 │ Secure reneg. (RFC 5746)  │
│ Heartbleed    │ OpenSSL 1.0.1-1.0.1f     │ CVE-2014-0160 │ Parchear y rotar claves   │
└───────────────┴──────────────────────────┴───────────────┴───────────────────────────┘

Downgrade y cifrado exportable

La idea común es forzar al canal a usar algo débil que ninguna de las dos partes usaría por decisión propia. Un atacante en el medio modifica la negociación para utilizar una versión anterior.

  • POODLE (CVE-2014-3566, octubre de 2014): fuerza un downgrade a SSL 3.0 y explota que su relleno CBC es no determinista, montando un padding oracle que recupera texto byte a byte. Fue el tiro de gracia a SSLv3. Curiosamente su CVSS es bajo (3.4) por lo laborioso del ataque.
  • FREAK (CVE-2015-0204) y Logjam (CVE-2015-4000), ambos de 2015: explotan los cifrados EXPORT de 512 bits, una reliquia de cuando EE.UU. restringía la exportación de criptografía fuerte. FREAK degrada a RSA_EXPORT; Logjam a DHE_EXPORT, reescribiendo el ClientHello. Una clave de 512 bits se factoriza hoy con recursos modestos.
  • DROWN (CVE-2016-0800, marzo de 2016): el más elegante y el más didáctico. Basta con que SSLv2 siga activo en algún servicio que comparta la clave para descifrar tráfico TLS moderno mediante un oráculo Bleichenbacher entre protocolos. Un protocolo viejo activo en cualquier parte rompe el nuevo.

Padding oracle y modo CBC

Los modos CBC de TLS 1.0–1.2 cifran y autentican en dos pasos separados (MAC-then-encrypt), y esto filtra información por el tiempo o por el relleno.

  • BEAST (CVE-2011-3389, 2011): aprovecha que en SSLv3/TLS 1.0 los vectores de inicialización CBC son predecibles, montando un ataque JavaScript en el navegador de la víctima.
  • Lucky13 (CVE-2013-0169, 2013): un canal lateral de tiempo en la verificación del MAC cuando el relleno CBC está malformado. Es difícil de explotar, pero la conclusión es la misma: abandonar CBC y usar AEAD.

Ataques de compresión

Comprimir antes de cifrar filtra información: si un dato secreto y uno adivinado se comprimen mejor juntos, la longitud del cifrado lo delata. Son de los más contraintuitivos.

  • CRIME (CVE-2012-4929, 2012): abusa de la compresión de TLS para robar cookies. Se cierra desactivando la compresión TLS, y por eso hoy viene desactivada por defecto.
  • BREACH (CVE-2013-3587, presentado en Black Hat 2013): la vuelta de tuerca. Ataca la compresión HTTP (gzip), que no puedes desactivar sin más porque la web depende de ella. Roba secretos reflejados en la respuesta, como tokens CSRF. La mitigación no es quitar gzip, sino no reflejar secretos junto a inputs del usuario y enmascarar los tokens.

Cifrados débiles por diseño

No hay downgrade ni truco: el cifrado en sí es el que falla.

  • Sweet32 (CVE-2016-2183, 2016): 3DES y DES usan bloques de 64 bits, y a las ~2³² operaciones (unos 32 GB en una sesión larga) aparece una colisión de cumpleaños que recupera texto. Se cierra quitando 3DES y DES.
  • RC4 (CVE-2015-2808, "Bar Mitzvah", 2015): el keystream de RC4 tiene sesgos estadísticos que filtran los primeros bytes. El RFC 7465 directamente lo prohíbe.

Renegociación insegura

  • CVE-2009-3555 (2009): TLS y SSLv3 no asociaban el handshake de renegociación con la conexión previa, y un atacante podía inyectar texto plano al principio de una sesión ajena. El arreglo fue el RFC 5746 (secure renegotiation); la buena práctica añade deshabilitar la renegociación iniciada por el cliente.

ROBOT — El regreso de Bleichenbacher

ROBOT (Return Of Bleichenbacher's Oracle Threat, 2017) es la reaparición, casi veinte años después, del ataque de Bleichenbacher contra el relleno PKCS#1 v1.5 del intercambio de claves RSA. La mitigación de fondo es abandonar el intercambio de claves RSA y usar ECDHE; TLS 1.3 lo elimina de raíz porque ya no ofrece ese intercambio.

Heartbleed

Heartbleed (CVE-2014-0160, abril de 2014) es el que hay que separar de todos los anteriores: no es un fallo del protocolo, sino de una implementación. Un error en la extensión Heartbeat de OpenSSL 1.0.1 a 1.0.1f permitía leer memoria del proceso más allá de lo debido, filtrando desde cookies hasta claves privadas del servidor. Por eso su mitigación tiene dos partes que la gente olvida: parchear OpenSSL y rotar las claves y certificados, porque hay que asumir que la clave privada quedó expuesta.

Análisis de TLS

Para obtener el certificado de un sitio mediante openssl, se pueden utilizar los siguientes comandos:

$ echo Q | openssl s_client -connect site.com:443                 # por defecto: TLSv1.3
$ echo Q | openssl s_client -connect site.com:443 -tls1_2         # forzar una versión

sslscan da la matriz completa de protocolos y cifrados de un tirón:

$ sslscan --no-colour site.com:443

Y testssl, el más completo para vulnerabilidades, permite un análisis exhaustivo del protocolo, cifrados, caducidad, vulnerabilidades, etc.

$ testssl -U https://site.com

Luego, el siempre confiable Nmap se puede utilizar de la siguiente manera.

$ nmap --script ssl-enum-ciphers -p 443 site.com

Correcta configuración

Encontrar el fallo es media auditoría; la otra media es decir cómo se arregla. La referencia práctica del oficio es el generador de configuración de Mozilla, que define dos perfiles: Modern (solo TLS 1.3) e Intermediate (TLS 1.2 + 1.3, el recomendado para uso general).

Lo importante no es copiar el bloque, sino entender que cada suite que sobra es un ataque abierto. Todas las suites recomendades por ese perfil son ECDHE/DHE (forward secrecy) y AEAD (GCM/CHACHA20-POLY1305), y esa sola decisión cierra por diseño POODLE, BEAST, Lucky13, Sweet32, RC4, FREAK y ROBOT.

Tres piezas más que cierran el HTTPS bien puesto:

  • Strict-Transport-Security (HSTS): obliga al navegador a usar siempre HTTPS. Su ausencia deja la puerta al downgrade a HTTP en la primera visita. El sitio de la serie lo emite con includeSubDomains; preload.
  • Contenido mixto: una página HTTPS que carga recursos por HTTP. Los navegadores actuales bloquean el contenido activo (scripts, iframes, CSS) y auto-actualizan a HTTPS el pasivo (imágenes, audio). La cabecera Content-Security-Policy: upgrade-insecure-requests fuerza todo a HTTPS.
  • Cookies con Secure: para que nunca viajen por un canal sin cifrar.