Inspeccionar fácilmente HTTPS (comunicación TLS) con curl -vvv
Hola, soy un inútil.
Introducción
HTTPS en sí mismo es una comunicación TLS, y como HTTP sobre TLS, es una comunicación HTTP envuelta en una comunicación TLS. Si nos centramos solo en la comunicación TLS, la solicitud a un dominio compatible con HTTPS sería diferente, por lo que al depurar detalladamente con curl -vvv, podemos verificar esa comunicación desde el client hello. Dicho esto, lo encontré por casualidad.
Echemos un vistazo
Bueno, echemos un vistazo de la siguiente manera.
curl -vvv -sl
-vvv # Opción de depuración detallada
-s # Modo silencioso, opcional
-I # Solo la salida de encabezados a la salida estándar
Entonces, veámoslo.
$ curl -vvv https://soulminingrig.com/ -sI
19:30:07.019276 [0-x] == Info: [READ] client_reset, clear readers
19:30:07.064770 [0-0] == Info: Host soulminingrig.com:443 was resolved.
19:30:07.065031 [0-0] == Info: IPv6: (none)
19:30:07.065180 [0-0] == Info: IPv4: 167.179.75.206
19:30:07.065413 [0-0] == Info: [HTTPS-CONNECT] adding wanted h2
19:30:07.065587 [0-0] == Info: [HTTPS-CONNECT] added
19:30:07.065718 [0-0] == Info: [HTTPS-CONNECT] connect, init
19:30:07.065881 [0-0] == Info: Trying 167.179.75.206:443...
19:30:07.066065 [0-0] == Info: [HTTPS-CONNECT] connect -> 0, done=0
19:30:07.066197 [0-0] == Info: [HTTPS-CONNECT] Curl_conn_connect(block=0) -> 0, done=0
19:30:07.066400 [0-0] == Info: [HTTPS-CONNECT] adjust_pollset -> 1 socks
19:30:07.083945 [0-0] == Info: [HTTPS-CONNECT] connect -> 0, done=0
19:30:07.084308 [0-0] == Info: [HTTPS-CONNECT] Curl_conn_connect(block=0) -> 0, done=0
19:30:07.084750 [0-0] == Info: [HTTPS-CONNECT] adjust_pollset -> 1 socks
19:30:07.095330 [0-0] == Info: [SSL] cf_connect()
19:30:07.095574 [0-0] == Info: [SSL] ossl_connect, step1
19:30:07.098502 [0-0] == Info: ALPN: curl offers h2,http/1.1
19:30:07.098614 [0-0] == Info: [SSL] ossl_connect, step2
19:30:07.099069 [0-0] => Send SSL data, 5 bytes (0x5)
0000: .....
19:30:07.099205 [0-0] == Info: TLSv1.3 (OUT), TLS handshake, Client hello (1):
19:30:07.099360 [0-0] => Send SSL data, 1563 bytes (0x61b)
~omitido~
Y apareció una gran cantidad de texto...
Verificación de la comunicación
Aquí se pudo realizar la resolución de nombres DNS en el puerto 443.
19:30:07.064770 [0-0] == Info: Host soulminingrig.com:443 was resolved.
Aquí se solicitó y se pudo establecer una conexión con h2, HTTP/2.
19:30:07.065413 [0-0] == Info: [HTTPS-CONNECT] adding wanted h2
19:30:07.065587 [0-0] == Info: [HTTPS-CONNECT] added
19:30:07.065718 [0-0] == Info: [HTTPS-CONNECT] connect, init
19:30:07.065881 [0-0] == Info: Trying 167.179.75.206:443...
19:30:07.066065 [0-0] == Info: [HTTPS-CONNECT] connect -> 0, done=0
Y finalmente comenzó la comunicación SSL/TLS.
19:30:07.095330 [0-0] == Info: [SSL] cf_connect()
19:30:07.095574 [0-0] == Info: [SSL] ossl_connect, step1
19:30:07.098502 [0-0] == Info: ALPN: curl offers h2,http/1.1
19:30:07.098614 [0-0] == Info: [SSL] ossl_connect, step2
19:30:07.099069 [0-0] => Send SSL data, 5 bytes (0x5)
Y entonces, ¡finalmente comenzó el handshake TLS del client hello en el protocolo con TLS 1.3!
19:30:07.099205 [0-0] == Info: TLSv1.3 (OUT), TLS handshake, Client hello (1):
Como envío del cliente, comienza la transmisión de paquetes.
19:30:07.099205 [0-0] == Info: TLSv1.3 (OUT), TLS handshake, Client hello (1):
19:30:07.099360 [0-0] => Send SSL data, 1563 bytes (0x61b)
0000: .........r.q.....GC...._4....k..C.P... ..?...x...HT..09..z4b....
0040: ..,9....<.......,.0.........+./...$.(.k.#.'.g.....9.....3.....=.
0080: <.5./..................soulminingrig.com........................
00c0: .............h2.http/1.1.........1.....6.4......................
0100: ...............................+........-.....3...........RIw..7
En este punto, el dominio soulminingrig.com se envía en texto plano en la propia solicitud, y si el servidor es compatible con SNI, la coherencia se mantiene.
19:30:07.105068 [0-0] == Info: [SSL] ossl_bio_cf_out_write(len=1568) -> 0, 1568
19:30:07.105244 [0-0] == Info: [SSL] ossl_bio_cf_in_read(len=5) -> 81, 0
19:30:07.105355 [0-0] == Info: [SSL] ossl_populate_x509_store, path=/etc/ssl/certs/ca-certificates.crt, blob=0
19:30:07.114557 [0-0] == Info: CAfile: /etc/ssl/certs/ca-certificates.crt
19:30:07.114669 [0-0] == Info: CApath: none
19:30:07.114768 [0-0] == Info: [SSL] SSL_connect() -> err=-1, detail=2
19:30:07.114903 [0-0] == Info: [SSL] SSL_connect() -> want recv
19:30:07.115050 [0-0] == Info: [SSL] cf_connect() -> 0, done=0
En este punto, el cliente está preparando el certificado raíz que posee.
La conexión aún no se ha establecido.
En el caso de Let's Encrypt, parece corresponder a ISRG Root X1.
Chain of Trust - Let’s Encrypt
$ grep -A 3 "ISRG Root X1" /etc/ssl/certs/ca-certificates.crt
# ISRG Root X1
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
Y entonces, ¡el Server Hello ha regresado!
19:30:07.178149 [0-0] == Info: [SSL] ossl_bio_cf_in_read(len=122) -> 0, 122
19:30:07.178363 [0-0] == Info: TLSv1.3 (IN), TLS handshake, Server hello (2):
19:30:07.178583 [0-0] <= Recv SSL data, 122 bytes (0x7a)
0000: ...v....y'...>........&.....,5O....... ..?...x...HT..09..z4b....
0040: ..,9.........+.....3.$... F........`~..l[..uhE..F.P?..V..6
19:30:07.179600 [0-0] == Info: [SSL] ossl_bio_cf_in_read(len=5) -> 0, 5
19:30:07.179730 [0-0] <= Recv SSL data, 5 bytes (0x5)
Y entonces, el certificado regresó del lado del servidor.
19:30:07.183139 [0-0] <= Recv SSL data, 2049 bytes (0x801)
0000: ...........0...0.................g...4...C..0...*.H.=...021.0...
0040: U....US1.0...U....Let's Encrypt1.0...U....E50...250714140356Z..2
0080: 51012140355Z0.1.0...U....soulminingrig.com0Y0...*.H.=....*.H.=..
00c0: ..B...V...$.....}.hN.f......n@F&...GR.....-.....?z]6d.=..<..eu..
Se envió una firma utilizando la clave privada que posee el servidor.
0000: .
19:30:07.199683 [0-0] == Info: TLSv1.3 (IN), TLS handshake, CERT verify (15):
19:30:07.199941 [0-0] <= Recv SSL data, 79 bytes (0x4f)
Aquí, el handshake TLS finalmente terminó y se estableció.
19:30:07.202057 [0-0] == Info: TLSv1.3 (IN), TLS handshake, Finished (20):
Handshake TLS poco visto
Al principio, incluso cuando preguntaba a ChatGPT u otros, me decían que usara Wireshark, pero como Wireshark a menudo genera mucho ruido, pensaba si no habría algo para verificarlo de forma sencilla. Entonces, recordé y lo intenté, y funcionó.
Podría ser interesante verificar los patrones de fallos del handshake TLS en el siguiente sitio:
badssl.com
$ curl -vvv -sl https://wrong.host.badssl.com/
19:50:53.161049 [0-x] == Info: [READ] client_reset, clear readers
19:50:53.202041 [0-0] == Info: Host wrong.host.badssl.com:443 was resolved.
19:50:53.202233 [0-0] == Info: IPv6: (none)
19:50:53.202326 [0-0] == Info: IPv4: 104.154.89.105
19:50:53.202496 [0-0] == Info: [HTTPS-CONNECT] adding wanted h2
~~~
19:50:53.759424 [0-0] == Info: SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256 / secp256r1 / rsaEncryption
19:50:53.759894 [0-0] == Info: ALPN: server accepted http/1.1
19:50:53.760124 [0-0] == Info: [SSL] ossl_connect, step3
19:50:53.760337 [0-0] == Info: Server certificate:
19:50:53.760541 [0-0] == Info: subject: CN=*.badssl.com
19:50:53.760763 [0-0] == Info: start date: Jul 15 20:02:58 2025 GMT
19:50:53.761029 [0-0] == Info: expire date: Oct 13 20:02:57 2025 GMT
19:50:53.761406 [0-0] == Info: subjectAltName does not match hostname wrong.host.badssl.com
19:50:53.761853 [0-0] == Info: SSL: no alternative certificate subject name matches target hostname 'wrong.host.badssl.com'
19:50:53.762409 [0-0] == Info: [SSL] cf_connect() -> 60, done=0
En este caso, se ha obtenido un certificado para *.badssl.com, pero debería haberse obtenido un certificado wildcard para *.host.badssl.com, así que está dando un error por ser incorrecto.
Hasta la próxima. Saludos.