SSL Handshake Failed: причины ошибки и пошаговая диагностика
SSL Handshake Failed: причины ошибки и пошаговая диагностика
Ошибка «SSL handshake failed» — собирательное название для десятка разных проблем, возникающих во время установки TLS-соединения. Клиент и сервер не смогли договориться о параметрах: версии протокола, cipher suite, сертификате или SNI. В этой статье разберём все типичные причины, покажем команды для диагностики и дадим алгоритм, который за 15 минут приведёт к первопричине.
Что такое TLS handshake и где он может сломаться
TLS handshake — это многоступенчатый обмен сообщениями между клиентом и сервером перед началом шифрованной передачи данных. В TLS 1.2 он занимает 2 RTT, в TLS 1.3 — 1 RTT. На каждом шаге может произойти сбой:
- ClientHello: клиент присылает список поддерживаемых версий TLS, cipher suites и SNI.
- ServerHello: сервер выбирает версию и cipher, отправляет свой сертификат.
- Certificate verification: клиент проверяет цепочку сертификатов, срок действия, домен.
- Key exchange: стороны договариваются о сессионном ключе (ECDHE, RSA).
- Finished: обе стороны подтверждают согласованные параметры.
Ошибка handshake — это провал на одном из этих шагов. Чтобы найти, где именно, нужен подробный лог. Запустите:
openssl s_client -connect example.com:443 -servername example.com -tlsextdebug -status
# или в verbose-режиме
curl -vI https://example.com 2>&1 | grep -E "TLS|SSL|handshake"
Причина 1: несовместимые версии протокола
Самая частая причина — клиент поддерживает только TLS 1.0/1.1, а сервер давно отключил эти версии (и правильно сделал). Проверьте, какие версии принимает сервер:
for v in tls1 tls1_1 tls1_2 tls1_3; do
echo -n "$v: "
openssl s_client -connect example.com:443 -$v < /dev/null 2>&1 | grep "Cipher is"
done
Если клиент — старый Java (до 8u261), iOS < 11, Windows XP, Android < 4.4 — он не поддерживает TLS 1.2+. Решение: обновить клиент или (в крайнем случае) временно разрешить TLS 1.1 на сервере до миграции. Подробнее о миграции — в статье «TLS 1.3 vs TLS 1.2: миграция».
Причина 2: несовпадение SNI
SNI (Server Name Indication) — расширение TLS, позволяющее хостить несколько SSL/TLS проверку-сайтов на одном IP. Клиент должен в ClientHello указать, к какому хосту подключается. Если SNI не указан или указан неверный — сервер либо вернёт дефолтный сертификат, либо откажет в handshake:
# БЕЗ SNI — часто даёт handshake failure
openssl s_client -connect example.com:443
# С SNI — работает
openssl s_client -connect example.com:443 -servername example.com
Проблема встречается у старых curl, wget, скриптов на Python 2, IoT-устройств. Обновите клиент или явно передавайте SNI.
Причина 3: несовместимые cipher suites
Клиент и сервер должны иметь хотя бы один общий cipher suite. Если вы ужесточили конфигурацию nginx до AEAD-only, старые клиенты (например, с RSA-key-exchange) отвалятся. Проверьте, какие ciphers предлагает сервер:
nmap --script ssl-enum-ciphers -p 443 example.com
Безопасный и совместимый Mozilla Intermediate-профиль для nginx:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:\
ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:\
ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
Подробнее о выборе cipher suites — в статье «Слабые cipher suites».
Причина 4: проблема с сертификатом сервера
Handshake может упасть из-за проблем самого сертификата:
- Просрочен или ещё не действителен — см. NET::ERR_CERT_DATE_INVALID.
- Неполная цепочка (отсутствует intermediate) — см. incomplete chain.
- Самоподписанный — см. self-signed certificate.
- CN/SAN не покрывает запрашиваемый домен.
- Сертификат отозван (CRL/OCSP).
Полная диагностика цепочки:
openssl s_client -connect example.com:443 -servername example.com -showcerts < /dev/null \
| openssl verify -CApath /etc/ssl/certs
Причина 5: mutual TLS (клиентский сертификат)
Если сервер настроен на mTLS, он требует от клиента собственный сертификат. Без него handshake падает с CertificateRequired. В nginx это выглядит так:
ssl_client_certificate /etc/nginx/ca.crt;
ssl_verify_client on; # или optional
Клиент должен передать сертификат:
curl --cert client.crt --key client.key https://api.example.com
Причина 6: MITM-прокси, антивирусы, корпоративные файрволы
На клиенте может стоять прокси, который перехватывает TLS (Kaspersky, ESET, корпоративный Zscaler). Он подменяет сертификат своим, а клиент этого не ожидает. Симптомы: «self-signed certificate in chain» в curl, хотя сайт нормальный. Решение — добавить CA прокси в доверенные или отключить проверку только для тестов (никогда в проде).
Алгоритм диагностики за 15 минут
- Запустите SSL Checker enterno.io — получите общую оценку и список проблем.
openssl s_client -connect host:443 -servername host— смотрите на первую ошибку.- Проверьте версии протокола и cipher suites (см. выше).
- Проверьте срок действия и цепочку сертификата.
- Попробуйте с другого клиента / другой сети (исключить MITM-прокси).
- Сверьте конфиг nginx/Apache с Mozilla SSL Config Generator.
Часто задаваемые вопросы
Handshake работает с curl, но падает с браузером — почему?
Скорее всего проблема в OCSP stapling или в неподдерживаемом cipher. Браузер строже, чем curl. Запустите тест на SSL Labs — он симулирует handshake десятков разных клиентов.
Как включить подробный лог TLS-ошибок в nginx?
error_log /var/log/nginx/error.log debug; + ssl_debug on; в новых версиях. Смотрите на строки с «SSL_do_handshake() failed».
TLS 1.3 всегда совместим со старыми клиентами?
Нет. Многие клиенты до 2018 года TLS 1.3 не поддерживают. Рекомендуется одновременно держать TLS 1.2 и 1.3.
Ошибка «alert handshake failure» — что это?
Это generic-сообщение от OpenSSL. Нужен более детальный лог — либо со стороны сервера (nginx debug), либо openssl s_client -tlsextdebug -msg со стороны клиента.
Заключение
SSL handshake failed — это всегда конкретная причина: версия, cipher, сертификат, SNI или mTLS. Методичная проверка через openssl за 15 минут доводит до первопричины в 95% случаев. SSL Checker от enterno.io ускоряет первый шаг — запустите его перед ручной отладкой, чтобы сразу исключить очевидное. Постоянный мониторинг сайтов через Monitors предупредит об ухудшении конфигурации после деплоя.
TLS 1.3 — RFC 8446. SNI — RFC 6066. Генератор конфигов — Mozilla SSL Config Generator.
Проверьте ваш сайт прямо сейчас
Проверить →