// Engineering Log

HTTP, HTTPS и TLS: Часть 10 — Отладка TLS: curl, openssl s_client и разбор ошибок сертификатов

Опубликовано 16.10.2026

// Быстрый маршрут

Эта статья относится к теме Безопасность и защита.

Большинство проблем с HTTPS сводится к нескольким причинам: неполная цепочка сертификатов, просроченный сертификат, неверное имя в сертификате, несовпадение версий или шифров, вмешательство промежуточного оборудования. Браузер обычно показывает общее «Подключение не защищено», а точную причину дают два инструмента: curl и openssl s_client. В этой части — как ими пользоваться и как читать их вывод.

Что показывает curl -v

bash
curl -sv -o /dev/null https://example.ru/ 2>&1 | grep '^\*'
* Host example.ru:443 was resolved.
*   Trying 192.0.2.10:443...
* Connected to example.ru (192.0.2.10) port 443
* 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, Certificate (11):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519 / RSASSA-PSS
* ALPN: server accepted h2
* Server certificate:
*  subject: CN=example.ru
*  start date: Sep  1 00:00:00 2026 GMT
*  expire date: Nov 30 23:59:59 2026 GMT
*  subjectAltName: host "example.ru" matched cert's "example.ru"
*  issuer: C=US; O=Let's Encrypt; CN=R11
*  SSL certificate verify ok.

Из вывода видно: к какому IP подключились, какая версия TLS и шифр согласованы, какой протокол выбран через ALPN, кому и кем выдан сертификат, до какого числа он действует и совпало ли имя. Формат вывода немного отличается в зависимости от TLS-библиотеки, с которой собран curl (OpenSSL, LibreSSL, GnuTLS, Schannel в Windows): curl --version показывает её в первой строке.

Ключи curl для TLS

bash
curl --tlsv1.2 --tls-max 1.2 https://example.ru/   # только TLS 1.2
curl --tlsv1.3 https://example.ru/                  # не ниже TLS 1.3
curl --ciphers ECDHE-RSA-AES128-GCM-SHA256 --tls-max 1.2 https://example.ru/   # конкретный шифр TLS 1.2
curl --cacert internal-ca.crt https://intranet.example.ru/   # доверять своему CA
curl --cert client.crt --key client.key https://admin.example.ru/   # клиентский сертификат (mTLS)
curl -k https://example.ru/                         # не проверять сертификат — только для диагностики

Ключ --tlsv1.2 означает «не ниже 1.2», а не «ровно 1.2». Чтобы проверить, поддерживает ли сервер именно 1.2, нужен ещё --tls-max 1.2.

-k (--insecure) полезен, чтобы отделить проблему сертификата от остальных: если с -k всё работает, дело в сертификате. Оставлять -k в скриптах нельзя.

openssl s_client

s_client — TLS-клиент из OpenSSL, который выполняет рукопожатие и показывает всё, что прислал сервер.

bash
openssl s_client -connect example.ru:443 -servername example.ru </dev/null
  • -servername задаёт SNI. В OpenSSL 1.1.1 и новее SNI берётся из -connect автоматически, если там имя, а не IP. Если вы подключаетесь по IP, указывайте -servername явно, иначе получите сертификат по умолчанию;
  • </dev/null закрывает ввод, чтобы команда завершилась сразу после рукопожатия.

Главное в выводе:

Certificate chain
 0 s:CN=example.ru
   i:C=US, O=Let's Encrypt, CN=R11
 1 s:C=US, O=Let's Encrypt, CN=R11
   i:C=US, O=Internet Security Research Group, CN=ISRG Root X1
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
...
Verify return code: 0 (ok)
  • Certificate chain — что прислал сервер: s — кому выдан, i — кем. У каждого следующего s должен совпадать с i предыдущего. Если в цепочке только строка 0, сервер не отдаёт промежуточный сертификат;
  • Verify return code: 0 (ok) — цепочка проверена. Любое другое значение — ошибка с пояснением.

Краткий вывод и частные случаи:

bash
openssl s_client -connect example.ru:443 -brief </dev/null          # только версия, шифр, проверка
openssl s_client -connect example.ru:443 -showcerts </dev/null      # все сертификаты цепочки в PEM
openssl s_client -connect example.ru:443 -tls1_2 </dev/null         # только TLS 1.2
openssl s_client -connect example.ru:443 -alpn h2 </dev/null        # проверить ALPN
openssl s_client -connect example.ru:443 -status </dev/null         # ответ OCSP, если сервер его прикладывает
openssl s_client -connect mail.example.ru:587 -starttls smtp </dev/null   # TLS в почтовых протоколах

-starttls работает для smtp, imap, pop3, ftp, xmpp, postgres, mysql и других протоколов, где TLS включается командой внутри открытого соединения.

Сведения о сертификате

Срок действия, имена и издатель сертификата на сервере:

bash
openssl s_client -connect example.ru:443 -servername example.ru </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates -ext subjectAltName
subject=CN=example.ru
issuer=C=US, O=Let's Encrypt, CN=R11
notBefore=Sep  1 00:00:00 2026 GMT
notAfter=Nov 30 23:59:59 2026 GMT
X509v3 Subject Alternative Name:
    DNS:example.ru, DNS:www.example.ru

Ключ -ext есть в OpenSSL 1.1.1 и новее. Для файла сертификата на диске — openssl x509 -in cert.pem -noout -text.

Проверить, истечёт ли сертификат в ближайшие 14 дней (код возврата 1 — истечёт):

bash
openssl s_client -connect example.ru:443 -servername example.ru </dev/null 2>/dev/null \
  | openssl x509 -noout -checkend $((14*24*3600)) && echo ok || echo 'истекает'

Для постоянного контроля лучше мониторинг: Prometheus blackbox_exporter отдаёт метрику probe_ssl_earliest_cert_expiry — дату истечения самого раннего сертификата в цепочке, и по ней настраивается оповещение.

Проверить, что ключ соответствует сертификату (хеши должны совпасть):

bash
openssl x509 -in cert.pem -noout -pubkey | openssl sha256
openssl pkey -in privkey.pem -pubout | openssl sha256

Проверить цепочку из файлов:

bash
openssl verify -untrusted chain.pem cert.pem

Какие версии и шифры поддерживает сервер

Вручную — перебором -tls1, -tls1_1, -tls1_2, -tls1_3 в s_client. Удобнее готовые инструменты:

bash
nmap --script ssl-enum-ciphers -p 443 example.ru
testssl.sh example.ru

testssl.sh проверяет версии, шифры, цепочку, HSTS, известные уязвимости и работает без установки, из одного скрипта. Для публичных сайтов то же делает SSL Labs.

Расшифровать трафик в Wireshark

Как записывать трафик через tcpdump и работать с Wireshark, описано в статье «Сетевой траблшутинг: tcpdump, sngrep и Wireshark».

Иногда нужно увидеть сам HTTP внутри TLS: что именно отправляет приложение, какие заголовки уходят. Для этого клиент записывает ключи сессий в файл, а Wireshark ими расшифровывает трафик.

bash
export SSLKEYLOGFILE=$HOME/tls-keys.log
curl https://example.ru/            # curl, собранный с OpenSSL, BoringSSL или GnuTLS
# или запустите Chrome/Firefox из этой же оболочки

В Wireshark: Настройки → Protocols → TLS → (Pre)-Master-Secret log filename — укажите файл. Трафик, записанный в это время, покажется расшифрованным, включая HTTP/2.

Файл ключей позволяет расшифровать всю записанную переписку — храните его как пароль и удаляйте после отладки. Системный curl в macOS собран с LibreSSL и может не поддерживать SSLKEYLOGFILE; используйте curl из Homebrew.

Частые ошибки и что они значат

unable to get local issuer certificate (curl, код 60; в s_client — verify error:num=20). Клиент не может построить цепочку до доверенного корня. Причины:

  • сервер не отдаёт промежуточный сертификат — исправьте на сервере: fullchain.pem вместо cert.pem;
  • сертификат выпущен внутренним CA, которого нет у клиента — --cacert или установка корня в систему;
  • в системе устаревшее хранилище корневых сертификатов — обновите пакет ca-certificates.

certificate has expired (код 10). Проверьте даты. Если сертификат на сервере свежий, а клиент видит старый — веб-сервер не перечитал сертификат после продления (nginx -s reload) или ответ идёт с другого узла балансировщика.

no alternative certificate subject name matches target host name (curl, код 60). Имени нет в SAN. Частые причины: заход на www. при сертификате только на домен; подключение без SNI или по IP; сертификат по умолчанию от другого сайта на этом сервере.

self-signed certificate / self-signed certificate in certificate chain (коды 18 и 19). Самоподписанный сертификат, или цепочка заканчивается корнем, которому клиент не доверяет. Если в издателе стоит имя антивируса или корпоративного шлюза, трафик расшифровывается по пути.

tlsv1 alert protocol version, unsupported protocol (curl, код 35). Нет общей версии TLS: старый клиент (только TLS 1.0) и современный сервер, или наоборот. Проверьте --tlsv1.2 --tls-max 1.2 и -tls1_2 в s_client.

sslv3 alert handshake failure (код 35). Нет общего шифра или группы, либо сервер требует клиентский сертификат. В выводе s_client с -state видно, на каком шаге рукопожатие оборвалось.

Connection reset by peer сразу после ClientHello. Сервер или оборудование по пути сбросило соединение, увидев ClientHello. Если по IP (без SNI) соединение проходит, а с именем — нет, это признак фильтрации по SNI на пути. Бывает и на корпоративных межсетевых экранах, которые не пропускают большой ClientHello с постквантовой группой X25519MLKEM768: проверьте с -groups x25519 в s_client или --curves x25519 в curl.

SSL routines::wrong version number в s_client. Вы подключились по TLS к порту, где TLS нет: например, к порту 80, или к 587, где нужен -starttls smtp, или сервер на 443 отвечает обычным HTTP.

Браузер открывает, а curl или приложение — нет. Почти всегда неполная цепочка: браузеры достраивают промежуточные сертификаты сами, библиотеки — нет. Проверка: openssl s_client -showcerts и строка Certificate chain.

Порядок диагностики

  1. curl -sv -o /dev/null https://имя/ — доходит ли до TLS, на каком шаге ошибка, код ошибки curl.
  2. openssl s_client -connect имя:443 -servername имя — цепочка и Verify return code.
  3. openssl x509 -noout -dates -ext subjectAltName — сроки и имена.
  4. curl --resolve имя:443:IP — ведут ли себя одинаково все серверы за балансировщиком.
  5. testssl.sh имя — версии, шифры и полная проверка настроек.
  6. Если ничего не помогло — запись трафика и Wireshark с SSLKEYLOGFILE.

// Похожая задача

Если у вас похожая ситуация

Эта статья относится к одной из рабочих тем. Можно продолжить чтение по теме, перейти на главную, чтобы понять, чем я занимаюсь, или сразу открыть услуги.

Тема статьи

Безопасность и защита

SSL, hardening, доступы, защита сервисов и безопасные конфигурации.

Часто с этим приходят

  • Настроить SSL, сертификаты и безопасные подключения
  • Ограничить доступы и закрыть лишние точки входа
  • Усилить конфигурацию сервера и сервисов

// Следующий шаг

Если вам нужна не только статья, а помощь по этой теме, удобнее сразу перейти в услугу. Главная и подборка материалов остаются рядом.

Открыть услуги

// Contact

Нужна помощь?

Свяжись со мной и я помогу решить проблему

Написать в Telegram

Отвечаю в течение рабочего дня (03:00–13:00 GMT)

Или оставьте заявку здесь:

Подтвердите, что вы не бот.

Написать и получить быстрый ответ