Что такое процесс рукопожатия (handshake) в HTTPS?

«Что такое процесс рукопожатия (handshake) в HTTPS?» — вопрос из категории Безопасность, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

HTTPS Handshake (рукопожатие TLS/SSL) — это процесс установления безопасного соединения между клиентом (например, браузером) и сервером перед началом обмена зашифрованными данными. Его цель — аутентификация сервера (а иногда и клиента) и безопасная выработка общего секретного ключа для симметричного шифрования.

Основные этапы TLS 1.2 Handshake:

flowchart LR
    A[Клиент] <--> B[Сервер]
    subgraph Шаг 1: Приветствие и согласование
        A -- "Client Hello<br>Поддерживаемые шифры, версии TLS,<br>client_random" --> B
        B -- "Server Hello<br>Выбранный шифр, сертификат,<br>server_random" --> A
    end
    subgraph Шаг 2: Проверка и обмен ключами
        A -- "Проверяет сертификат<br>Генерирует pre_master_secret,<br>шифрует открытым ключом сервера" --> B
    end
    subgraph Шаг 3: Завершение и шифрование
        A -- "Change Cipher Spec, Finished" --> B
        B -- "Change Cipher Spec, Finished" --> A
        A <-- "Зашифрованное приложение<br>данные" --> B
    end
  1. Client Hello: Клиент отправляет случайное число (client_random), список поддерживаемых шифров и версий TLS.
  2. Server Hello: Сервер отвечает своим случайным числом (server_random), выбранным набором шифров и своим SSL-сертификатом, содержащим его открытый ключ.
  3. Проверка сертификата: Клиент проверяет сертификат (срок действия, подпись доверенным центром сертификации - CA, соответствие домену).
  4. (Client Key Exchange): Клиент генерирует pre_master_secret, шифрует его открытым ключом сервера из сертификата и отправляет.
  5. Выработка ключей: И клиент, и сервер независимо вычисляют master_secret из client_random, server_random и pre_master_secret, а затем на его основе генерируют одинаковые ключи для симметричного шифрования и MAC.
  6. Finished: Стороны обмениваются зашифрованными сообщениями Finished, подтверждая, что handshake прошел успешно и ключи совпадают.

Пример кода на C# (установка безопасного соединения):

using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

// Создание TcpClient и обертывание потока в SslStream
TcpClient client = new TcpClient("api.example.com", 443);
SslStream sslStream = new SslStream(client.GetStream(), false,
    new RemoteCertificateValidationCallback(ValidateServerCertificate), // Callback для проверки
    null);

try
{
    // Вызов AuthenticateAsClient инициирует TLS handshake
    sslStream.AuthenticateAsClient("api.example.com");
    // После успешного handshake можно писать/читать в sslStream
}
catch (AuthenticationException e)
{
    Console.WriteLine($"Ошибка аутентификации: {e.Message}");
}

// Callback для кастомной проверки сертификата (опционально)
private static bool ValidateServerCertificate(object sender, X509Certificate certificate,
    X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
    if (sslPolicyErrors == SslPolicyErrors.None) return true;
    // Логика обработки ошибок (например, пропуск самоподписанных сертификатов для dev)
    return false; // В продакшене всегда возвращать false при ошибках
}

Эволюция и оптимизации:

  • TLS 1.3: Значительно ускорен handshake, часто до 1 RTT (один обмен). Удалены небезопасные алгоритмы шифрования.
  • Session Resumption (TLS Session Tickets): Позволяет повторно использовать параметры предыдущей сессии для быстрого восстановления соединения без полного handshake.
  • False Start: Клиент может начать отправлять зашифрованные данные до получения подтверждения Finished от сервера, уменьшая задержку.

Критически важные аспекты для разработчика:

  1. Сертификаты: Всегда используйте валидные сертификаты от доверенных CA в продакшене. Самоподписанные сертификаты — только для разработки и тестирования.
  2. Алгоритмы: Настройте сервер на использование современных, безопасных шифров (отключите SSLv3, TLS 1.0/1.1, слабые алгоритмы).
  3. HTTP Strict Transport Security (HSTS): Заголовок, принудительно указывающий браузеру использовать только HTTPS для данного домена.