Envías una petición a https://api.example.com/pedidos. Antes de que un controlador pueda leer el JSON, el cliente necesita resolver una pregunta: ¿estoy hablando con el servidor esperado y puedo enviarle datos sin que alguien en el camino los lea o modifique?
TLS participa en esa respuesta. Pero no funciona simplemente «cifrando todo con el certificado». Durante la conexión intervienen una negociación, un acuerdo de claves, pruebas de identidad y claves simétricas con propósitos diferentes.
Recorreremos una conexión nueva de TLS 1.3 con certificado de servidor y acuerdo de claves efímero, sin certificado de cliente ni datos anticipados. Después la reproduciremos en un laboratorio local con OpenSSL. Separar este caso de sus variantes evita trasladar a TLS 1.3 explicaciones que describen otros protocolos o versiones.
HTTP describe la conversación; HTTPS protege su transporte
HTTP define operaciones y representaciones: métodos como GET y POST, encabezados, códigos de estado y cuerpos. Por sí solo no proporciona un canal confidencial ni autentica criptográficamente al servidor. El esquema HTTPS exige proteger la comunicación y comprobar la identidad del servicio. La semántica de un POST no cambia por usarlo. RFC 9110, sección 4.2.2.
En nuestro ejemplo, HTTP/1.1 viaja sobre TLS y TLS sobre TCP. HTTP/2 también puede usar TLS sobre TCP. HTTP/3 usa QUIC, que integra el handshake de TLS 1.3, pero protege paquetes mediante mecanismos propios: no es la misma pila con un registro TLS colocado encima de UDP. RFC 9001, sección 4.
Tampoco se ejecuta un handshake completo por cada petición. Una conexión puede reutilizarse para varias operaciones HTTP. Y el extremo TLS puede ser un proxy, balanceador o CDN, no necesariamente el proceso que implementa la API. Si ese intermediario abre otra conexión al backend, hay que proteger y validar ese segundo tramo por separado.
Qué aporta TLS y de quién te protege
TLS, Transport Layer Security, establece un canal entre dos extremos. Sus objetivos incluyen confidencialidad, integridad y autenticación del interlocutor. En el uso web habitual se autentica al servidor; autenticar al cliente mediante certificado es una opción adicional. RFC 8446, sección 1.
Imagina que un observador controla una red Wi-Fi por la que pasa tu petición:
- El cifrado busca impedir que lea el token o el cuerpo HTTP durante ese tramo.
- La autenticación de los datos permite detectar alteraciones, en lugar de entregar al controlador un mensaje modificado silenciosamente.
- La comprobación de identidad evita aceptar como servidor legítimo a cualquier equipo que sea capaz de responder por la red.
Estas propiedades dependen de una implementación y una validación correctas. Si el cliente acepta cualquier certificado, puede terminar hablando por un canal cifrado con el intermediario equivocado.
El handshake completo, paso a paso
Un handshake es el intercambio inicial que establece los parámetros y secretos de la conexión. Este diagrama representa nuestro caso sobre TCP; cada flecha es un mensaje lógico, no necesariamente un paquete de red:
No se pudo mostrar el diagrama. Puedes consultar su definición.
sequenceDiagram
participant C as Cliente
participant S as Servidor TLS
C->>S: ClientHello
S->>C: ServerHello
Note over C,S: Derivan claves de handshake
S->>C: EncryptedExtensions
S->>C: Certificate
S->>C: CertificateVerify
S->>C: Finished
Note over C: Verifica identidad y handshake
C->>S: Finished
C->>S: Petición HTTP protegida
S->>C: Respuesta HTTP protegida
Después de ServerHello, los mensajes de handshake del diagrama ya van cifrados. TLS 1.3 separa la negociación del cifrado y hash de la del acuerdo de claves y las firmas; elimina el transporte estático de claves RSA. RFC 8446, secciones 1.2 y 2.
1. ClientHello: capacidades y una contribución pública
El cliente anuncia versiones mediante supported_versions, suites de cifrado y algoritmos de firma admitidos. También declara grupos de acuerdo de claves en supported_groups y puede incluir contribuciones públicas ya calculadas en key_share.
Para nuestro laboratorio elegiremos X25519, un mecanismo de acuerdo de claves basado en curvas elípticas. Cada extremo conserva un valor privado efímero y envía su contribución pública. No publica el secreto final ni envía una contraseña al servidor.
El ClientHello puede incorporar otras extensiones: SNI indica el nombre del servicio al que se quiere conectar; ALPN ofrece protocolos de aplicación, como HTTP/1.1 o HTTP/2. Anunciar un nombre con SNI no demuestra que el servidor tenga derecho a usarlo. Cloudflare: separación de negociaciones en TLS 1.3, OpenSSL: opciones de s_client.
2. ServerHello: selección y acuerdo de claves
El servidor elige parámetros compatibles y devuelve su contribución pública. En el caso de X25519, cada extremo combina su propio valor privado con la contribución pública recibida y obtiene el mismo secreto compartido. Un observador que solo tenga las contribuciones públicas no debería poder calcularlo bajo los supuestos criptográficos del mecanismo. RFC 7748, sección 6.1.
Esto todavía no prueba quién está al otro lado: un acuerdo de claves sin autenticación no basta frente a un intermediario activo. La autenticación posterior vincula el intercambio con la identidad esperada.
Si el servidor necesita una contribución de otro grupo ofrecido por el cliente, puede responder con HelloRetryRequest, solicitando un segundo ClientHello. No todas las conexiones siguen exactamente las flechas del caso sencillo. RFC 8446, sección 2.1.
3. EncryptedExtensions: parámetros dentro del canal cifrado
Ambos extremos ya pueden derivar claves para proteger el resto del handshake. EncryptedExtensions transporta parámetros que no eran necesarios para iniciar ese contexto criptográfico, como la selección ALPN del servidor.
Por eso «ya está cifrado» y «ya comprobé la identidad» son momentos distintos. La aplicación no debe confundir la disponibilidad de claves con la aceptación del servidor.
4. Certificate y CertificateVerify: identidad y posesión de la clave
Certificate presenta el certificado del servidor y, normalmente, certificados intermedios que ayudan a construir la cadena de confianza. Un certificado contiene una clave pública y datos de identidad; no contiene la clave privada del servidor.
CertificateVerify incluye una firma que vincula la clave privada correspondiente con el transcript del handshake: el historial de mensajes intercambiados. Copiar un certificado público no permite generar esa prueba. Después se verifica Finished, un código de autenticación calculado con material derivado del handshake; no es otra firma del certificado. RFC 8446, secciones 4.4.3 y 4.4.4.
En nuestro ejemplo el cliente valida esos mensajes, envía su propio Finished y luego transmite HTTP protegido. No presenta un certificado de cliente: el servidor no ha autenticado por ello al usuario de la API.
Un certificado no se vuelve confiable por estar firmado
Una autoridad certificadora, o CA, firma certificados que vinculan una clave pública con determinados identificadores bajo su política de emisión. En la infraestructura pública, el cliente intenta construir una cadena desde el certificado presentado hasta una raíz que ya acepta como confiable.
La raíz no se vuelve confiable porque el servidor la envíe. La confianza procede del almacén y la política del cliente. El servidor suele enviar su certificado y los intermedios necesarios; el cliente ya dispone de sus anclas de confianza. La construcción y validación de esa cadena incluye firmas, vigencia, restricciones y propósito de uso. OpenSSL: verificación de certificados.
Además hay que comprobar el nombre que el cliente esperaba, no uno elegido libremente por el servidor. Para nombres DNS se compara con identificadores adecuados en Subject Alternative Name, o SAN. RFC 9525 descarta usar el campo Common Name como sustituto para esta verificación; una conexión por dirección IP requiere la identidad IP correspondiente, no tratarla como un nombre DNS. RFC 9525, secciones 4 y 6.
Por ejemplo, un certificado válido para pagos.example.com no autoriza a presentarse como api.example.com. Y -servername api.example.com en OpenSSL selecciona SNI: no reemplaza -verify_hostname api.example.com.
Las comprobaciones de revocación y sus fallos dependen del cliente y su política; no supongas que toda biblioteca consulta una CA en línea en cada conexión. Tampoco un certificado válido demuestra que un sitio sea honesto, que no tenga vulnerabilidades o que el usuario tenga permiso para operar.
Qué hace cada pieza criptográfica
En el laboratorio veremos parámetros distintos porque resuelven problemas distintos:
| Pieza | Elección del laboratorio | Función |
|---|---|---|
| Acuerdo de claves | X25519 | Obtener material secreto compartido sin transmitirlo directamente |
| Clave del certificado y firma del servidor | ECDSA con P-256 | Probar posesión de la clave privada asociada al certificado |
| Suite TLS 1.3 | TLS_AES_128_GCM_SHA256 | Elegir AES-128-GCM y SHA-256 para los usos definidos por TLS |
| Derivación | HKDF | Producir secretos y claves separados por etapa y dirección |
| Protección del tráfico | AES-GCM | Cifrar registros y autenticar su contenido |
La suite no dice «X25519» ni «ECDSA»: esas decisiones se negocian por separado. En su nombre, 128 corresponde a la longitud de la clave AES; SHA256 no significa que los datos viajen solamente hasheados. Cloudflare: negociación criptográfica.
TLS 1.3 todavía puede usar certificados con claves RSA para firmas compatibles, como RSA-PSS. Lo que no hace es el antiguo transporte de una premaster secret cifrada con RSA. Tampoco sería correcto afirmar que todo TLS 1.2 utilizaba ese transporte: TLS 1.2 también admitía acuerdos efímeros. RFC 9325, secciones 4.1 y 4.2.
Del secreto compartido a varias claves de tráfico
Una KDF, Key Derivation Function, es una función de derivación de claves. HKDF utiliza HMAC y una estructura de extracción y expansión para obtener material destinado a usos diferentes. No es una función lenta para almacenar contraseñas. RFC 5869, secciones 1 y 2.
El esquema de TLS incorpora secretos y hashes del transcript. Separa las etapas de handshake y aplicación, y cada dirección de comunicación tiene su propio material de tráfico. A partir de esos secretos se obtienen claves e IV; no hay una única «clave de sesión» que sirva indistintamente para todo. RFC 8446, secciones 7.1 y 7.3.
Esta separación también explica por qué cambiar la negociación altera los valores que los extremos verifican. Para desarrollar una API no debes reproducir estas fórmulas manualmente: deja el protocolo a una biblioteca TLS mantenida.
Cifrado autenticado y secreto hacia adelante
AEAD significa cifrado autenticado con datos asociados. Permite proteger el contenido y comprobar su integridad, incluyendo determinados datos que se autentican sin cifrarse. No equivale a cifrar y añadir un hash público al final. RFC 5116, sección 2.
TLS aplica esa protección a registros; no firma cada petición HTTP con la clave del certificado. Si la verificación del registro falla, sus datos no deben entregarse como una petición válida. Los detalles de clave, nonce y tag se relacionan con los conceptos que desarrollamos en AES-GCM desde cero, aunque TLS define su propio formato y construcción de nonces.
El acuerdo efímero permite secreto hacia adelante: si posteriormente se filtra la clave privada de firma del certificado, eso por sí solo no permite descifrar las conversaciones pasadas capturadas, siempre que los secretos efímeros se hayan eliminado y no se hayan expuesto las claves de tráfico. No protege frente a un atacante que ya controla un extremo mientras procesa los datos. RFC 9325, sección 7.3.
Reanudación, 0-RTT y otras variantes
Nuestro recorrido no representa todas las conexiones. La reanudación puede autenticar con una PSK, clave precompartida derivada de una sesión anterior, sin volver a enviar la cadena de certificados. Puede combinarse con un nuevo acuerdo efímero; un modo solo PSK tiene propiedades distintas.
0-RTT permite enviar ciertos datos anticipados al reanudar. Esos datos no tienen las mismas garantías de secreto hacia adelante ni la misma protección frente a replay que el tráfico normal: un atacante puede intentar repetirlos. No habilites operaciones con efectos sensibles en early data sin un diseño explícito de protección. RFC 8446, secciones 2.2, 2.3 y 8.
También existe autenticación mutua mediante certificados, o mTLS. Añade autenticación del cliente al canal; no decide automáticamente a qué pedidos, cuentas o recursos puede acceder. La autorización por recurso sigue siendo una responsabilidad de la aplicación.
Laboratorio local: observar un handshake y rechazar errores
Usaremos OpenSSL 3.x, dos terminales y una shell compatible con Bash o Zsh. No probaremos sistemas de terceros ni modificaremos el almacén de confianza del sistema. El servidor escuchará solo en 127.0.0.1:8443 y devolverá una página de diagnóstico; no es una API de producción.
Comprueba qué ejecutable estás usando en ambas terminales:
openssl versionSi aparece LibreSSL u otra herramienta, selecciona una instalación mantenida de OpenSSL 3 antes de continuar. En la ejecución de esta guía se utilizó OpenSSL 3.6.3. Las versiones y políticas de tu instalación pueden cambiar los detalles de la salida.
1. Crear una identidad exclusiva del laboratorio
En una carpeta nueva:
mkdir laboratorio-tls
cd laboratorio-tls
umask 077
openssl req -x509 -newkey ec \
-pkeyopt ec_paramgen_curve:P-256 \
-noenc -days 2 \
-keyout servidor.key \
-out servidor.crt \
-subj '/CN=localhost' \
-addext 'subjectAltName=DNS:localhost' \
-addext 'basicConstraints=critical,CA:FALSE' \
-addext 'keyUsage=critical,digitalSignature' \
-addext 'extendedKeyUsage=serverAuth'Generamos un certificado autofirmado con SAN localhost y vigencia corta. -noenc deja la clave privada del laboratorio sin contraseña; umask 077 restringe los permisos de los archivos nuevos. No uses esta identidad en producción ni subas servidor.key a Git. OpenSSL: req.
El laboratorio confiará explícitamente en ese certificado mediante -CAfile. Eso no lo convierte en una CA pública ni instala una raíz global. Nos permite separar confianza y coincidencia de nombre sin montar una infraestructura de certificados completa.
2. Iniciar el extremo TLS
En la primera terminal, desde esa carpeta:
openssl s_server \
-accept 127.0.0.1:8443 \
-cert servidor.crt \
-key servidor.key \
-tls1_3 \
-groups X25519 \
-ciphersuites TLS_AES_128_GCM_SHA256 \
-alpn http/1.1 \
-num_tickets 0 \
-wwwFijamos los parámetros para que el ejemplo sea fácil de comparar. No es una lista universal de producción. -www responde a GET / con información de diagnóstico y -num_tickets 0 evita emitir tickets de reanudación en este ejercicio. OpenSSL: s_server.
3. Validar y enviar una petición
En la segunda terminal, entra en la misma carpeta y ejecuta:
printf 'GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n' |
openssl s_client \
-connect 127.0.0.1:8443 \
-servername localhost \
-verify_hostname localhost \
-CAfile servidor.crt \
-verify_return_error \
-tls1_3 \
-groups X25519 \
-alpn http/1.1 \
-brief -ign_eofLa dirección de conexión, el nombre SNI y el nombre que validamos son parámetros diferentes. Aquí conectamos por loopback y esperamos la identidad DNS localhost.
-verify_return_error es esencial: s_client es una herramienta de diagnóstico y, sin esa opción, puede continuar pese a errores de verificación. -ign_eof permite seguir leyendo cuando printf termina; el servidor del ejemplo cierra tras responder. OpenSSL: verificación y uso no interactivo de s_client.
Estos son campos seleccionados de la salida observada, sin el resto de la página de diagnóstico:
Protocol version: TLSv1.3
Ciphersuite: TLS_AES_128_GCM_SHA256
Signature type: ecdsa_secp256r1_sha256
Verification: OK
Verified peername: localhost
Peer Temp Key: X25519, 253 bitsTambién se recibió HTTP/1.0 200 ok. Ese HTTP/1.0 corresponde a la respuesta del servidor de diagnóstico: no contradice haber negociado TLS 1.3. Son versiones de protocolos diferentes. La cifra junto a X25519 tampoco es el tamaño de la clave AES.
4. Ver la secuencia de mensajes
Repite la conexión registrando los mensajes del protocolo:
printf 'GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n' |
openssl s_client \
-connect 127.0.0.1:8443 \
-servername localhost \
-verify_hostname localhost \
-CAfile servidor.crt \
-verify_return_error \
-tls1_3 \
-groups X25519 \
-alpn http/1.1 \
-msg -msgfile handshake.log \
-quiet -ign_eof > respuesta.txt 2> validacion.logAbre handshake.log. En la prueba se observó esta secuencia, omitiendo los bytes hexadecimales y otros registros:
>>> ClientHello
<<< ServerHello
<<< EncryptedExtensions
<<< Certificate
<<< CertificateVerify
<<< Finished
>>> Finished>>> significa enviado por el cliente; <<<, recibido. OpenSSL muestra mensajes interpretados desde el extremo de la conexión: ver Certificate en este registro no significa que viajara en claro. Tampoco -msg equivale a una captura pasiva de paquetes.
Puedes encontrar etiquetas heredadas de versión o un ChangeCipherSpec de compatibilidad. No concluyas que se negoció TLS 1.2 solo por esos elementos; comprueba la versión negociada. TLS 1.3 conserva ciertos campos por compatibilidad. RFC 8446, apéndice D.4.
5. Comprobar dos rechazos distintos
Primero, conserva la confianza en el certificado, pero exige otro nombre:
openssl s_client \
-connect 127.0.0.1:8443 \
-servername localhost \
-verify_hostname otro.localhost \
-CAfile servidor.crt \
-verify_return_error \
-tls1_3 -brief < /dev/null
echo $?Después, exige el nombre correcto, pero no cargues certificados de confianza:
openssl s_client \
-connect 127.0.0.1:8443 \
-servername localhost \
-verify_hostname localhost \
-no-CAfile -no-CApath -no-CAstore \
-verify_return_error \
-tls1_3 -brief < /dev/null
echo $?Las opciones -no-CA... vacían las fuentes de confianza de ese comando; no desactivan la verificación ni cambian la configuración del sistema. Los resultados observados fueron:
| Prueba | Resultado |
|---|---|
| Certificado confiado y nombre correcto | Verificación correcta y respuesta HTTP |
| Nombre incorrecto | hostname mismatch, salida 1 |
| Sin confianza en el certificado autofirmado | self-signed certificate, salida 1 |
Un certificado desconocido y un nombre incorrecto requieren diagnósticos diferentes. Ninguno se corrige adecuadamente haciendo que el cliente acepte todo.
Qué no resuelve HTTPS
El canal protege el tráfico entre sus extremos TLS. Después de descifrarlo, un proxy, la aplicación o sus logs pueden exponerlo. Los tokens, datos personales y claves siguen necesitando controles de almacenamiento y registro.
HTTPS tampoco evita por sí solo una inyección SQL, un acceso indebido a un objeto, un token mal validado o un error de lógica. Un usuario autenticado puede enviar una petición no autorizada por una conexión perfectamente válida. Por eso TLS complementa la validación de JWT y los controles de la API.
Un observador de red aún puede conocer direcciones IP, volúmenes y tiempos. HTTPS no cifra automáticamente las consultas DNS; en el handshake convencional mostrado, ClientHello también puede revelar el nombre SNI. Otros mecanismos pueden reducir exposición de metadatos, pero no los hemos configurado en este laboratorio.
Un dominio de phishing también puede tener un certificado válido para su propio nombre. La validación comprueba el servicio solicitado; no decide si el usuario eligió el dominio correcto.
Errores de configuración que conviene detectar temprano
- Anular la verificación para resolver un fallo de certificados.
curl -k,rejectUnauthorized: falseoNODE_TLS_REJECT_UNAUTHORIZED=0no reparan la confianza ni el nombre. En entornos privados, configura las CA apropiadas y conserva la verificación. curl: certificados de servidor. - Comprobar solo el cifrado o solo la cadena. Un handshake exitoso en una herramienta permisiva no demuestra identidad válida. Usa un cliente que rechace los fallos y compruebe el nombre esperado.
- Olvidar intermedios, renovaciones o el reloj. Vigila expiración y errores de cadena; prueba el mismo cliente y almacén de confianza que usará la aplicación.
- Proteger únicamente el tramo público. Verifica cómo se conecta el proxy al origen y quién puede acceder a ese recorrido.
- Confundir el selector de suites de TLS 1.2 con el de TLS 1.3. En OpenSSL,
-ciphersuitesconfigura estas últimas;-ciphercontrola suites de TLS 1.2 y anteriores. OpenSSL: opciones del servidor. - Habilitar versiones antiguas o 0-RTT sin un criterio de compatibilidad y riesgo. TLS 1.2 correctamente configurado no es equivalente a SSL o TLS 1.0; evalúa los requisitos actuales de tus clientes y prioriza TLS 1.3 donde sea compatible. RFC 9325, sección 3.
OWASP recomienda usar TLS en todo el sitio, cuidar certificados y evitar contenido mixto. HSTS puede reforzar el uso de HTTPS en navegadores compatibles; no sustituye la validación de certificados ni constituye una política universal para todos los clientes de una API. Son recomendaciones de despliegue que debes adaptar al servicio, no obligaciones legales por citar una guía. OWASP TLS Cheat Sheet.
Antes de confiar en una petición
Detén el servidor con Ctrl+C. Desde la carpeta del laboratorio, elimina los archivos generados cuando ya no los necesites:
rm servidor.key servidor.crt handshake.log respuesta.txt validacion.logNo se instaló ninguna CA en el sistema. El ejercicio comprobó un handshake, una identidad local y dos fallos de validación; no auditó una PKI pública, revocación, reanudación, 0-RTT, mTLS ni la configuración de un servicio de producción.
Al diagnosticar una API, separa tres preguntas: ¿se negoció el protocolo esperado?, ¿se validó la identidad del extremo correcto?, ¿la aplicación autoriza esta operación? TLS responde a las dos primeras dentro de su alcance. La tercera empieza cuando la petición llega al código de negocio.
Referencias
- RFC 8446: TLS 1.3: handshake, autenticación, registros y derivación de claves.
- RFC 9525: identidad del servicio y RFC 9325: recomendaciones de uso de TLS.
- RFC 7748: X25519, RFC 5869: HKDF y RFC 5116: AEAD.
- RFC 9110: HTTP y RFC 9001: TLS en QUIC.
- OpenSSL: s_client, s_server, req y verificación.
- Cloudflare: análisis de TLS 1.3, curl: certificados y OWASP: TLS.
