TLS handshake — как устанавливается защищённое соединение
TLS handshake — как устанавливается защищённое соединение
Мы знаем, что TLS шифрует данные гибридным подходом. Но как клиент и сервер договариваются о ключах, не встречаясь заранее, и как клиент убеждается, что перед ним настоящий сервер? Ответ — TLS handshake (рукопожатие). Разберём этот процесс по шагам на примере TLS 1.3.
Зачем нужно рукопожатие
Перед тем как клиент и сервер начнут передавать зашифрованные данные, им нужно:
- Договориться о версии TLS и наборе алгоритмов (cipher suite).
- Аутентифицировать сервер (клиент должен убедиться, что это настоящий
example.com, а не атакующий). - Создать общий сессионный ключ для симметричного шифрования.
Всё это происходит за 1 round-trip в TLS 1.3. Round-trip — это обмен сообщениями туда и обратно: клиент посылает пакет, сервер отвечает, клиент снова посылает. Один round-trip добавляет задержку, равную времени прохождения сигнала до сервера и обратно (RTT).
Шаг 1: ClientHello
Клиент (браузер) начинает рукопожатие, отправляя сообщение ClientHello:
Client → Server: ClientHello
Версия: TLS 1.3
Случайное число: client_random (32 байта)
Поддерживаемые cipher suites: TLS_AES_256_GCM_SHA384, TLS_AES_128_GCM_SHA256, TLS_CHACHA20_POLY1305_SHA256
Поддерживаемые группы ключей: x25519, P-256, P-384
Параметры для DH (публичный ключ клиента в выбранной группе)
server_name (SNI): example.com
Клиент заранее отправляет свой DH-публичный ключ в ClientHello — это оптимизация TLS 1.3, позволяющая сократить рукопожатие. SNI (Server Name Indication) сообщает серверу, к какому домену обращается клиент, что критично при виртуальном хостинге (один сервер обслуживает несколько доменов с разными сертификатами).
Шаг 2: ServerHello
Сервер получает ClientHello, выбирает из предложенных опций и отвечает:
Server → Client: ServerHello
Выбранная версия: TLS 1.3
Выбранный cipher suite: TLS_AES_256_GCM_SHA384
Случайное число: server_random (32 байта)
Параметры для DH (публичный ключ сервера)
Server → Client: EncryptedExtensions
Дополнительные параметры (например, ALPN — какой протокол: h2, http/1.1)
Server → Client: Certificate
Сертификат сервера (цепочка: leaf → intermediate CA → root CA)
Server → Client: CertificateVerify
Подпись всего рукопожатия закрытым ключом сервера — доказательство владения сертификатом
Server → Client: Finished
MAC всего рукопожатия — подтверждение целостности
Важный момент: в TLS 1.3 всё, кроме ServerHello, шифруется. Перехватчик видит только выбор версии и cipher suite, но не сертификат сервера — это дополнительная защита приватности.
Шаг 3: Проверка сертификата клиентом
Получив сертификат, клиент должен его проверить. Это ключевой шаг для аутентичности:
- Проверить цепочку сертификатов. Сертификат подписан Intermediate CA, чей сертификат подписан Root CA. Root CA встроены в браузер или ОС.
- Проверить подпись CertificateVerify. Убедиться, что сервер владеет закрытым ключом, соответствующим сертификату.
- Проверить домен. Сертификат должен быть выдан на домен
example.com(или*.example.com). - Проверить срок действия. Сертификат не истёк (обычно выдаётся на 90 дней).
- Проверить отзыв. Сертификат не отозван (через CRL или OCSP).
Если любая проверка не проходит — браузер показывает предупреждение «Ваше соединение не защищено» и предлагает пользователю решить, продолжать ли (а иногда и не предлагает).
Шаг 4: Завершение рукопожатия
Если проверка пройдена, клиент завершает рукопожатие:
Client → Server: Finished
MAC всего рукопожатия
На этом этапе обе стороны вычислили общий сессионный ключ из client_random, server_random и результата DH. Рандомные числа от обеих сторон гарантируют, что даже если DH-ключи повторно использовать, сессионный ключ будет разным (добавляют энтропию).
Теперь можно передавать данные приложения — HTTP-запросы и ответы, зашифрованные симметричным ключом.
Визуализация полного обмена (TLS 1.3)
Client Server
| |
|--- ClientHello ------------------->| (предлагает алгоритмы, DH public key)
| |
|<-- ServerHello --------------------| (выбирает алгоритмы, DH public key)
|<-- {EncryptedExtensions} ----------| (зашифровано)
|<-- {Certificate} -----------------| (сертификат сервера, зашифрован)
|<-- {CertificateVerify} -----------| (доказательство владения ключом)
|<-- {Finished} --------------------| (MAC рукопожатия)
| |
|--- {Finished} ------------------->| (MAC рукопожатия)
| |
|<==== Защищённые данные ===========>| (HTTP запросы и ответы)
К моменту отправки HTTP-запроса рукопожатие уже завершено, и весь трафик защищён.
Вычисление сессионных ключей
Из трёх компонентов — DH shared secret, client_random и server_random — стороны вычисляют несколько ключей через HKDF (HMAC-based Key Derivation Function):
master_secret = HKDF(DH_shared_secret, client_random, server_random)
Из master_secret выводятся:
- encryption_key_client (для шифрования client → server)
- encryption_key_server (для шифрования server → client)
- mac_key_client (для проверки целостности client → server)
- mac_key_server (для проверки целостности server → client)
Ключи для каждого направления разные — это важно. Если атакующий каким-то чудом узнает ключ шифрования клиента, он не сможет расшифровать ответы сервера. HKDF гарантирует, что даже если один из компонентов (например, client_random) предсказуем, итоговый ключ безопасен при условии секретности DH shared secret.
0-RTT для повторных соединений
TLS 1.3 поддерживает 0-RTT — клиент может отправить данные приложения уже в первом пакете, если ранее уже общался с этим сервером:
Client Server
|--- ClientHello + данные приложения + pre-shared key -->|
Это работает через механизм PSK (Pre-Shared Key): сервер выдаёт клиенту идентификатор сессии, и при следующем подключении клиент использует его, пропуская полное рукопожатие. 0-RTT полезно для мобильных приложений и повторных заходов на сайт, но данные 0-RTT уязвимы к replay-атакам (повторной отправке). Поэтому на 0-RTT отправляют только идемпотентные запросы (GET), но не POST.
Проверь себя
- Зачем в ClientHello отправляется публичный DH-ключ, а не ждать ответа сервера?
- Что проверяет клиент, получив сертификат сервера?
- Что означает, что в TLS 1.3 часть рукопожатия зашифрована?
- Это оптимизация: клиент заранее отправляет DH-ключ, чтобы обе стороны могли вычислить сессионный ключ за один round-trip, а не за два.
- Цепочку сертификатов до Root CA, подпись CertificateVerify (доказательство владения закрытым ключом), соответствие домену, срок действия, не отозван ли сертификат.
- Перехватчик не видит сертификат сервера и другие параметры, что защищает приватность (не даёт узнать, на какой именно домен заходит клиент) и усложняет анализ протокола для атак.
Что унести с урока
- TLS 1.3 рукопожатие — 1-RTT: ClientHello → ServerHello + Certificate + Finished → Client Finished.
- Клиент проверяет сертификат по цепочке, домену, сроку, подписи и отзыву.
- 0-RTT позволяет отправлять данные с первым пакетом при повторных соединениях, но уязвимо к replay.
- SNI (Server Name Indication) в ClientHello сообщает серверу, к какому домену обращается клиент.
В следующем уроке разберём подробнее сертификаты и центры сертификации — как работает PKI, почему сертификатов несколько в цепочке и что значит «доверенный корневой сертификат».