TCP-соединение открыто, но пока по нему всё едет открытым текстом. Прежде чем отправить запрос, браузер и сервер должны договориться о ключе шифрования так, чтобы его не узнал никто на пути, — и чтобы браузер убедился, что разговаривает именно с этим сайтом, а не с самозванцем. Это TLS. В первой части он обошёлся в 128 мс — дороже всех сетевых шагов. Разберём, за что платим.
Это седьмая часть цикла «Что происходит, когда открываешь сайт».
Что показывает curl
$ curl -sv -o /dev/null https://alekseychikun.ru/robots.txt
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_CHACHA20_POLY1305_SHA256 / X25519MLKEM768 / RSASSA-PSS
* ALPN: server accepted h2
Шаг за шагом
Client Hello. Клиент сразу выкладывает всё: какие шифры умеет, имя сайта (SNI), какие протоколы поверх готов говорить (ALPN: h2 и http/1.1) и — главное отличие TLS 1.3 от прежних версий — свою половину ключа. Клиент заранее угадывает, какой способ обмена ключами выберет сервер, и отправляет открытую часть для него. Поэтому второго круга не нужно.
Ответ сервера — один пакет за другим, 6383 байта:
Server Hello— выбранный шифр и вторая половина ключа. С этого момента обе стороны могут вычислить общий секрет, и всё дальнейшее уже зашифровано;Encrypted Extensions— остальные договорённости, например выбранный протоколh2;Certificate— паспорт сайта: сертификат и цепочка до доверенного центра (о ней в следующей части);CertificateVerify— подпись всего рукопожатия закрытым ключом сертификата. Это доказательство, что сервер действительно владеет ключом, а не просто показывает чужой сертификат;Finished— контрольная сумма всего разговора: никто не подменил ни байта.
Finished клиента — и сразу следом HTTP-запрос, в том же полёте. Итого TLS 1.3 стоит один круг по сети. В TLS 1.2 было два: сначала договаривались о шифре, потом обменивались ключами.
Куда делись 128 мс, если круг — 29
Из захвата: приветствие клиента ушло на 32-й миллисекунде, ответ сервера пришёл на 126-й. Круг по сети — 29 мс, значит около 65 мс сервер думал. На что:
- подпись RSA-ключом 4096 бит. Ключ сертификата этого сайта — RSA 4096 (видно в выводе
openssl). Подпись таким ключом — самая дорогая операция рукопожатия, а сервер у нас — ноутбук на i3 2013 года. Ключ ECDSA P-256 подписывает на порядок быстрее при сопоставимой стойкости; - обмен ключами X25519MLKEM768. Это гибрид: классический X25519 плюс постквантовый ML-KEM. Его теперь по умолчанию предлагают современные клиенты, отсюда и размер приветствия — 1,5 КБ вместо пары сотен байт. Вычисляется быстро, но данных по сети больше.
Во втором запросе из первой части TLS занял 69 мс вместо 128. Каждый запуск curl — новый процесс без памяти о прошлой сессии, так что это было полное рукопожатие, просто на «разогретом» сервере. Браузер устроен иначе: он сохраняет билет сессии и в следующий раз проходит сокращённый путь, без пересылки сертификата и тяжёлой подписи.
Почему ChaCha20, а не AES
В строке итога — шифр TLS_CHACHA20_POLY1305_SHA256. Обычно в TLS выбирают AES-GCM: на современных процессорах он ускорен аппаратно. Но у процессора нашего сервера аппаратного AES нет, и прокси (traefik, написан на Go) учитывает это: если у сервера нет ускорения AES, он предпочитает ChaCha20, который быстро считается обычными инструкциями. Шифр — не догма, а выбор под железо.
Посмотреть у своего сайта
# версия, шифр, обмен ключами, подпись
curl -sv -o /dev/null https://example.ru/ 2>&1 | grep -E 'SSL connection|ALPN'
# тип и длина ключа сертификата
echo | openssl s_client -connect example.ru:443 -servername example.ru 2>/dev/null \
| openssl x509 -noout -text | grep -E 'Public Key Algorithm|Public-Key'
# сколько стоит рукопожатие: время до конца TLS минус время TCP
curl -so /dev/null -w 'tcp=%{time_connect} tls=%{time_appconnect}\n' https://example.ru/
Если TLS стоит больше трёх кругов TCP — смотрите на тип ключа и на нагрузку сервера, а не на сеть.
Защищённое соединение открывается долго?
Пришлите адрес — разберу рукопожатие и подскажу, что поменять: ключ, шифры или настройки возобновления сессий.
// загружаю комментарии…