Глава 2. Тестирование средствами OpenSSL

Из-за большого количества функций протокола и особенностей реализации иногда бывает сложно определить точную конфигурацию и возможности защищенных серверов. Хотя для этой цели существует множество инструментов, зачастую нелегко получить адекватное представление о том, как они реализованы, и потому невозможно полностью полагаться на точность их результатов. Несмотря на то, что я потратил годы на тестирование защищенных серверов и в моём арсенале есть отличные инструменты, когда я действительно хочу понять, что происходит, я использую OpenSSL и Wireshark. Я не говорю, что вы должны использовать OpenSSL для повседневного тестирования; напротив, следует подобрать автоматизированный инструмент, которому вы можете доверять. Но когда вам действительно нужно быть в чём-то уверенным, единственный способ — это погрузиться в дебри вместе с OpenSSL.

Подключение к SSL-сервисам

OpenSSL поставляется с клиентским инструментом, который можно использовать для подключения к защищенному серверу. Этот инструмент похож на telnet или nc в том смысле, что он обрабатывает уровень SSL/TLS, но позволяет полностью контролировать следующий уровень.

Чтобы подключиться к серверу, нужно указать имя хоста и порт. Например:

$ openssl s_client -connect www.feistyduck.com:443

После ввода этой команды вы увидите вывод большого количества диагностической информации (подробнее об этом чуть позже), а затем вам предложат напечатать всё, что вы хотите. Поскольку мы обращаемся к HTTP-серверу, самое разумное, что можно сделать, — это отправить HTTP-запрос. В продолжение примера я использовал запрос HEAD, поскольку он предписывает серверу не отправлять тело ответа:

HEAD / HTTP/1.0
Host: www.feistyduck.com

HTTP/1.1 200 OK
Date: Tue, 10 Mar 2015 17:13:23 GMT
Server: Apache
Strict-Transport-Security: max-age=31536000
Cache-control: no-cache, must-revalidate
Content-Type: text/html;charset=UTF-8
Transfer-Encoding: chunked
Set-Cookie: JSESSIONID=7F3D840B9C2FDB1FF7E5731590BD9C99; Path=/; Secure; HttpOnly
Connection: close

read:errno=0

Мы убедились, что коммуникационный уровень TLS работает: через него мы подключились к HTTP-серверу, отправили запрос и получили ответ. Теперь давайте вернёмся к диагностическому выводу. В первых строках даётся информация о сертификате сервера:

CONNECTED(00000003)
depth=3 L = ValiCert Validation Network, O = "ValiCert, Inc.", OU = ValiCert Class 2 ↩
Policy Validation Authority, CN = http://www.valicert.com/, emailAddress = ↩
info@valicert.com
verify error:num=19:self signed certificate in certificate chain
verify return:0

В моей системе (возможно, и в вашей тоже) s_client не проверяет информацию по доверенным сертификатам из хранилища по умолчанию; он жалуется на то, что в цепочке сертификатов есть самоподписанный сертификат. В большинстве случаев нет нужды беспокоиться о проверке сертификата; но если вам это понадобится, нужно будет указать s_client на местонахождение доверенных сертификатов, вот так:

$ openssl s_client -connect www.feistyduck.com:443 -CAfile /etc/ssl/certs/ca-certificates.crt
CONNECTED(00000003)
depth=3 L = ValiCert Validation Network, O = "ValiCert, Inc.", OU = ValiCert Class 2 > ↩
Policy Validation Authority, CN = http://www.valicert.com/, emailAddress = ↩
info@valicert.com
verify return:1
depth=2 C = US, O = "Starfield Technologies, Inc.", OU = Starfield Class 2 ↩
Certification Authority
verify return:1
depth=1 C = US, ST = Arizona, L = Scottsdale, O = "Starfield Technologies, Inc.", OU = ↩
http://certificates.starfieldtech.com/repository, CN = Starfield Secure Certification ↩
Authority, serialNumber = 10688435
verify return:1
depth=0 1.3.6.1.4.1.311.60.2.1.3 = GB, businessCategory = Private Organization, ↩
serialNumber = 06694169, C = GB, ST = London, L = London, O = Feisty Duck Ltd, CN = ↩
www.feistyduck.com
verify return:1

Вместо жалоб s_client вы увидите, что он проверил каждый из сертификатов в цепочке. Чтобы подобная проверка работала, у вас должен быть доступ к хорошему набору сертификатов удостоверяющих центров. В примере я использовал путь (/etc/ssl/certs/ca-certificates.crt), корректный для Ubuntu 12.04 LTS, но в вашей системе он может быть другим. Если вы не хотите использовать предоставляемые в составе операционной системы сертификаты УЦ, можно положиться на те, которые предоставляет Mozilla, как обсуждается в разделе "Построение доверенного хранилища" в главе 1.

Предупреждение: операционная система OS X от Apple поставляется с модифицированной версией OpenSSL, которая иногда отменяет проверку сертификата. Другими словами, параметр -CAfile может работать не так, как ожидается. Это можно исправить, задав переменную окружения OPENSSL_X509_TEA_DISABLE перед вызовом s_client15. Учитывая то, что используемая по умолчанию в OS версия OpenSSL относится к устаревшей ветке 0.9.x, лучше всего обновить её до последней версии, например, с использованием Homebrew или MacPorts.

В следующем разделе вывода перечисляются все предоставленные сервером сертификаты в порядке их доставки:

Certificate chain
 0 s:/1.3.6.1.4.1.311.60.2.1.3=GB/businessCategory=Private Organization↩
/serialNumber=06694169/C=GB/ST=London/L=London/O=Feisty Duck Ltd/CN=www.feistyduck.com
   i:/C=US/ST=Arizona/L=Scottsdale/O=Starfield Technologies, Inc./OU=http:/↩
/certificates.starfieldtech.com/repository/CN=Starfield Secure Certification Authority↩
/serialNumber=10688435
 1 s:/C=US/ST=Arizona/L=Scottsdale/O=Starfield Technologies, Inc./OU=http:/↩
/certificates.starfieldtech.com/repository/CN=Starfield Secure Certification Authority↩
/serialNumber=10688435
   i:/C=US/O=Starfield Technologies, Inc./OU=Starfield Class 2 Certification Authority
 2 s:/C=US/O=Starfield Technologies, Inc./OU=Starfield Class 2 Certification Authority
   i:/L=ValiCert Validation Network/O=ValiCert, Inc./OU=ValiCert Class 2 Policy ↩
Validation Authority/CN=http://www.valicert.com//emailAddress=info@valicert.com
 3 s:/L=ValiCert Validation Network/O=ValiCert, Inc./OU=ValiCert Class 2 Policy ↩
Validation Authority/CN=http://www.valicert.com//emailAddress=info@valicert.com
   i:/L=ValiCert Validation Network/O=ValiCert, Inc./OU=ValiCert Class 2 Policy ↩
Validation Authority/CN=http://www.valicert.com//emailAddress=info@valicert.com

Для каждого сертификата в первой строке выводится информация о субъекте сертификата, во второй — о его издателе.

Этот раздел очень полезен в случаях, когда вам нужно посмотреть какие именно сертификаты отправляются; средства просмотра сертификатов браузеров обычно отображают восстановленные цепочки сертификатов, которые могут почти полностью отличаться от предоставленных сервером. Чтобы определить, является ли цепочка номинально корректной, вы можете проверить, совпадают ли издатель сертификата с субъектом следующего в цепочке сертификата. Проверку нужно начинать с конечного сертификата (сертификата веб-сервера) и двигаться по цепочке вверх (в выводимом списке — сверху вниз). Последний издатель, которого вы увидите, может указывать на некоторый корневой сертификат, не входящий в цепочку, либо (если самоподписанный корневой сертификат был включён в цепочку) указывать сам на себя.

Следующий элемент в выводе — это сертификат сервера; он представляет собой довольно много текста, большая часть которого в примере удалена для краткости:

Server certificate
-----BEGIN CERTIFICATE-----
MIIF5zCCBM+gAwIBAgIHBG9JXlv9vTANBgkqhkiG9w0BAQUFADCB3DELMAkGA1UE
[удалёно 30 строк...]
os5LW3PhHz8y9YFep2SV4c7+NrlZISHOZVzN
-----END CERTIFICATE-----
subject=/1.3.6.1.4.1.311.60.2.1.3=GB/businessCategory=Private Organization↩
/serialNumber=06694169/C=GB/ST=London/L=London/O=Feisty Duck Ltd/CN=www.feistyduck.com
issuer=/C=US/ST=Arizona/L=Scottsdale/O=Starfield Technologies, Inc./OU=http:/↩
/certificates.starfieldtech.com/repository/CN=Starfield Secure Certification Authority↩
/serialNumber=10688435

Примечание: Если вместо имени в поле subject вы видите длинную строку чисел, это означает, что OpenSSL не знает рассматриваемый идентификатор объекта (OID). OID — это глобально уникальные и однозначные идентификаторы, которые используются для обозначения "вещей". К примеру, в предыдущем выводе OID 1.3.6.1.4.1.311.60.2.1.3 должен был быть заменён на jurisdictionOfIncorporationCountryName, используемое в сертификатах extended validation (EV).

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

Далее следуют многочисленные данные о соединении TLS, большая часть которых не требует пояснений:

---
No client certificate CA names sent
---
SSL handshake has read 3043 bytes and written 375 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1.1
    Cipher    : ECDHE-RSA-AES256-SHA
    Session-ID: 032554E059DB27BF8CD87EBC53E9FF29376265F0BBFDBBFB7773D2277E5559F5
    Session-ID-ctx:
    Master-Key: 1A55823368DB6EFC397DEE2DC3382B5BB416A061C19CEE162362158E90F1FB0846EEFDB2↩
CCF564A18764F1A98F79A768
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    0000 - 77 c3 47 09 c4 45 e4 65-90 25 8b fd 77 4c 12 da   w.G..E.e.%..wL..
    0010 - 38 f0 43 09 08 a1 ec f0-8d 86 f8 b1 f0 7e 4b a9   8.C..........~K.
    0020 - fe 9f 14 8e 66 d7 5a dc-0f d0 0c 25 fc 99 b8 aa   ....f.Z....%....
    0030 - 8f 93 56 5a ac cd f8 66-ac 94 00 8b d1 02 63 91   ..VZ...f......c.
    0040 - 05 47 af 98 11 81 65 d9-48 5b 44 bb 41 d8 24 e8   .G....e.H[D.A.$.
    0050 - 2e 08 2d bb 25 59 f0 8f-bf aa 5c b6 fa 9c 12 a6   ..-.%Y....\.....
    0060 - a1 66 3f 84 2c f6 0f 06-51 c0 64 24 7a 9a 48 96   .f?.,...Q.d$z.H.
    0070 - a7 f6 a9 6e 94 f2 71 10-ff 00 4d 7a 97 e3 f5 8b   ...n..q...Mz....
    0080 - 2d 1a 19 9c 1a 8d e0 9c-e5 55 cd be d7 24 2e 24   -........U...$.$
    0090 - fc 59 54 b0 f8 f1 0a 5f-03 08 52 0d 90 99 c4 78   .YT...._..R....x
    00a0 - d2 93 61 d8 eb 76 15 27-03 5e a4 db 0c 05 bb 51   ..a..v.'.^.....Q
    00b0 - 6c 65 76 9b 4e 6b 6c 19-69 33 2a bd 02 1f 71 14   lev.Nkl.i3*...q.

    Start Time: 1390553737
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)
---

Самая важная информация здесь — это версия протокола (TLS 1.1) и используемый набор шифров (ECDHE-RSA-AES256-SHA). Также вы можете определить, что сервер выдал вам идентификатор сеанса (Session-ID) и билет сеанса TLS (TLS session ticket) (способ возобновления сеансов без сохранения состояния сервера), а также то, что поддерживается безопасное повторное согласование параметров (Secure Renegotiation). После того как вы разберётесь с тем, что содержит весь этот вывод, вы будете редко обращать на него внимание.

Предупреждение: Часто в дистрибутивах операционных систем поставляются инструменты, отличающиеся от стандартных версий. Только что мы опять с этим столкнулись: предыдущая команда устанавливает соединение по протоколу TLS 1.1, даже несмотря на то, что сервер поддерживает TLS 1.2. Почему? Как выясняется, некоторые версии OpenSSL, поставляемые с Ubuntu 12.04 LTS, отключают TLS 1.2 для клиентских подключений, чтобы избежать определённых проблем совместимости. Чтобы не сталкиваться с подобными неожиданностями, я рекомендую всегда проводить тестирование с той версией OpenSSL, которую вы сами скомпилировали и настроили.

Тестирование протоколов с возможностью перехода на SSL

При использовании с протоколом HTTP, в TLS упаковывается весь передаваемый по каналу открытый трафик для формирования трафика HTTPS. Некоторые другие протоколы начинают обмен в открытом виде, а затем переходят на шифрованный обмен. Если вы хотите протестировать такой протокол, вам нужно сообщить OpenSSL, какой именно протокол вы будете использовать, чтобы он мог осуществить переход на шифрование в соответствии с вашими намерениями. Информация о протоколе передаётся с помощью параметра -starttls. Например:

$ openssl s_client -connect gmail-smtp-in.l.google.com:25 -starttls smtp

На момент написания этого материала поддерживались протоколы smtp, pop3, imap, ftp и xmpp.

Использование разных форматов протокола рукопожатия

Иногда при тестировании сервера с помощью OpenSSL ваши попытки связаться с ним могут завершиться неудачно, даже если вы знаете, что сервер поддерживает TLS (например, при использовании браузера TLS работает). Одна из возможных причин такой ситуации может заключаться в том, что сервер не поддерживает старый формат протокола рукопожатия SSL 2.

Поскольку OpenSSL пытается вести переговоры по правилам всех известных ему протоколов, а также потому, что SSL 2 может быть согласован только с использованием старого протокола рукопожатия SSL 2, по умолчанию OpenSSL использует этот вариант протокола рукопожатия. Даже несмотря на то, что он связан с очень старой и небезопасной версией протокола, данный старый формат протокола рукопожатия технически не является небезопасным. Он поддерживает повышение уровня протокола, а значит можно согласовать использование более совершенного протокола. Однако, многие возможности согласования соединений, разработанные после SSL 2, не поддерживаются этим форматом протокола рукопожатия.

Поэтому, если что-то не работает, и вы точно не знаете, в чём причина, можно попытаться заставить OpenSSL использовать более новый формат протокола рукопожатия. Это можно сделать, отключив поддержку SSL 2:

$ openssl s_client -connect www.feistyduck.com:443 -no_ssl2

Альтернативный способ добиться того же эффекта — указать желаемое имя сервера в командной строке:

$ openssl s_client -connect www.feistyduck.com:443 -servername www.feistyduck.com

Чтобы указать имя сервера, OpenSSL должен использовать функцию из более нового формата рукопожатия (она называется Server Name Indication [SNI]), и поэтому ему приходится отказываться от использования старого формата.

Извлечение удалённых сертификатов

Когда вы соединяетесь с удалённым сервером с использованием s_client, он выводит закодированный в PEM сертификат сервера в стандартный вывод. Если вам зачем-то требуется этот сертификат, его можно скопировать, прокрутив наверх буфер экрана. Если же сертификат — единственное, что вам требуется, можно использовать такую последовательность команд для сокращения вывода:

$ echo | openssl s_client -connect www.feistyduck.com:443 2>&1 | sed --quiet '/-BEGIN ↩
CERTIFICATE-/,/-END CERTIFICATE-/p' > www.feistyduck.com.crt

Первая команда echo нужна, чтобы отделить вашу оболочку командной строки от s_client. Если этого не сделать, s_client будет ожидать, что вы что-то введёте, пока сервер не вернёт таймаут (что потенциально может занять довольно много времени).

По умолчанию s_client будет выводить только конечный сертификат; если вы хотите вывести полную цепочку сертификатов, нужно указать параметр -showcerts. С этим параметром предыдущая последовательность команд поместит все сертификаты в один и тот же файл.

Тестирование поддерживаемых протоколов

По умолчанию s_client будет пытаться использовать лучшую из возможных версий протокола для связи с удаленным сервером и сообщит согласованную версию в выходных данных.

    Protocol  : TLSv1.1

Если вам нужно протестировать поддержку конкретной версии протокола, у вас есть два варианта. Вы можете явно выбрать протокол для тестирования, предоставив один из параметров -ssl2, -ssl3, -tls1, -tls1_1 или -tls1_2. Другой вариант: вы можете выбрать те протоколы, которые вы не хотите тестировать, используя один или несколько из следующих параметров: -no_ssl2, -no_ssl3, -no_tls1, -no_tls1_1 или -no_tls1_2.

Примечание: Не все версии OpenSSL поддерживают все версии протокола. Например, старые версии OpenSSL не будут поддерживать TLS 1.1 и TLS 1.2, а новые версии программы могут не поддерживать более старые протоколы, такие как SSL 2.

Например, такой вывод можно получить при тестировании сервера, не поддерживающего конкретную версию протокола:

$ openssl s_client -connect www.example.com:443 -tls1_2
CONNECTED(00000003)
140455015261856:error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number:s3↩
_pkt.c:340:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 5 bytes and written 7 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : 0000
    Session-ID:
    Session-ID-ctx:
    Master-Key:
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1339231204
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
---

Тестирование поддерживаемых наборов шифров

Если вы хотите использовать OpenSSL для определения того, поддерживает ли удалённый сервер конкретный набор шифров, потребуется небольшая хитрость. В OpenSSL можно указать строку конфигурации шифров, которая предназначена для того, чтобы выбрать, какие наборы шифров вы хотите использовать. Однако, если вы укажите в ней только один набор и успешно пройдёте процедуру рукопожатия с сервером, то удостоверитесь в том, что сервер поддерживает данный набор. Если же рукопожатие завершится неудачей, вы будете знать, что этот набор не поддерживается.

В качестве примера, для проверки поддержки сервером шифров RC4-SHA, выполните:

$ openssl s_client -connect www.feistyduck.com:443 -cipher RC4-SHA

Если вы хотите определить все наборы, поддерживаемые конкретным сервером, начните с выполнения команды openssl ciphers ALL для получения списка всех наборов шрифтов, поддерживаемых вашей версией OpenSSL. Затем отправьте на сервер запросы с каждым из них один за другим, чтобы проверить их по отдельности. Я не предлагаю вам делать это вручную; это тот самый случай, когда напрашивается небольшая автоматизация. Фактически, в данной ситуации вполне уместен поиск какого-нибудь подходящего для этой цели инструмента.

Однако у такого способа тестирования есть недостаток: вы сможете протестировать только те наборы шифров, которые поддерживает ваша версия OpenSSL. Раньше эта проблема стояла гораздо острее: до версии 1.0 OpenSSL поддерживал значительно меньшее количество наборов (например, на моем сервере с версией 0.9.8k их было всего 32). При использовании ветки 1.0.1 вы можете протестировать более 100 наборов (возможно, большую часть из вообще ныне существующих).

Ни одна из существующих библиотек SSL/TLS не поддерживает все наборы шифров, и поэтому провести всеобъемлющее тестирование весьма трудно. В SSL Labs для этой цели я применяю процедуру частичного рукопожатия с использованием самописного клиента, который делает вид, что поддерживает любые наборы шифров. На самом деле он не сможет выполнить согласование даже какого-то одного набора, но для того, чтобы сервер ответил, поддерживает ли он набор или нет, достаточно просто предложить согласование. Таким способом вы сможете не только проверить все наборы, но и сделать это очень эффективно.

Тестирование серверов, поддерживающих SNI

Изначально SSL и TLS были разработаны для поддержки только одного веб-сайта на одну конечную IP-точку (комбинацию IP-адреса и порта). SNI — это расширение TLS, позволяющее использовать более одного сертификата на одной конечной IP-точке. Клиенты TLS используют это расширение для отправки желаемого имени, а серверы TLS используют его для выбора нужного сертификата для отправки в ответ на такие запросы. Короче говоря, благодаря SNI стал возможным безопасный виртуальный хостинг.

Поскольку SNI ещё не очень широко используется серверами, в большинстве случаев вам не нужно указывать его в командной строке s_client. Но когда вы сталкиваетесь с системой, поддерживающей SNI, может произойти одно из трёх:

Задействовать SNI в s_client можно с помощью параметра -servername:

$ openssl s_client -connect www.feistyduck.com:443 -servername www.feistyduck.com

Определить, требуется ли для сайта SNI или нет, можно, протестировав его сначала с параметром для SNI, а затем без него. Если возвращаемые сертификаты на совпадают, SNI требуется.

Иногда, если запрошенное имя сервера недоступно, сервер сообщает об этом в предупреждении TLS. Даже несмотря на то, что это предупреждение не является фатальным и касается больше сервера, клиент может принять решение закрыть соединение. Например, в старых версиях OpenSSL (до 1.0.0) вы получите следующее сообщение об ошибке:

$ /opt/openssl-0.9.8k/bin/openssl s_client -connect www.feistyduck.com:443 -servername xyz.com
CONNECTED(00000003)
1255:error:14077458:SSL routines:SSL23_GET_SERVER_HELLO:reason(1112):s23_clnt.c:596:

Тестирование возможности повторного использования сессии

В сочетании с параметром -reconnect команда s_client может использоваться для тестирования повторного использования сессии. В этом режиме s_client будет подключаться к целевому серверу шесть раз; при первом подключении будет создана новая сессия, а затем будут выполняться попытки повторно использовать тот же сеанс в последующих пяти соединениях:

$ echo | openssl s_client -connect www.feistyduck.com:443 -reconnect

Эта команда выводит целое море информации, большая часть которой нам не интересна. Ключевыми её частями являются информация о новых и повторно используемых сессиях. Новая сессия должна быть только одна (в самом начале), на неё указывает следующая строка:

New, TLSv1/SSLv3, Cipher is RC4-SHA

Далее следуют пять возобновлений сессии, обозначаемых такими строками:

Reused, TLSv1/SSLv3, Cipher is RC4-SHA

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

$ echo | openssl s_client -connect www.feistyduck.com:443 -reconnect -no_ssl2 2> /dev/null | grep 'New\|Reuse'
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Reused, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Reused, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Reused, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Reused, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Reused, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384

Вот что делает эта последовательность команд:

Примечание: Если вы не хотите включать в тестирование сеансовые билеты (например, потому что ещё не все клиенты поддерживают эту функцию), их можно отключить с помощью параметра -no_ticket.

Проверка статуса отзыва сертификата с помощью OCSP

Если ответчик OCSP работает со сбоями, иногда трудно понять, в чём же именно причина. Проверка статуса отзыва сертификата из командной строки возможна, но это не совсем тривиальный процесс. Вам потребуется выполнить следующие шаги:

  1. Получить сертификат, который вы хотите проверить на отзыв.

  2. Получить сертификат издателя.

  3. Определить URL-адрес ответчика OCSP.

  4. Отправить OCSP-запрос и проанализировать ответ.

Для выполнения первых двух шагов подключитесь к серверу с указанием параметра -showcerts:

$ openssl s_client -connect www.feistyduck.com:443 -showcerts

Первый сертификат в выводе будет принадлежать серверу. Если цепочка сертификатов корректно сформирована, вторым сертификатом будет сертификат издателя, выпустившего серверный сертификат. Чтобы убедиться в этом, проверьте совпадение издателя первого сертификата с субъектом второго сертификата:

---
Certificate chain
 0 s:/1.3.6.1.4.1.311.60.2.1.3=GB/businessCategory=Private Organization↩
/serialNumber=06694169/C=GB/ST=London/L=London/O=Feisty Duck Ltd/CN=www.feistyduck.com
   i:/C=US/ST=Arizona/L=Scottsdale/O=Starfield Technologies, Inc./OU=http:/↩
/certificates.starfieldtech.com/repository/CN=Starfield Secure Certification Authority↩
/serialNumber=10688435
-----BEGIN CERTIFICATE-----
MIIF5zCCBM+gAwIBAgIHBG9JXlv9vTANBgkqhkiG9w0BAQUFADCB3DELMAkGA1UE
[30 строк текста удалены]
os5LW3PhHz8y9YFep2SV4c7+NrlZISHOZVzN
-----END CERTIFICATE-----
 1 s:/C=US/ST=Arizona/L=Scottsdale/O=Starfield Technologies, Inc./OU=http:/↩
/certificates.starfieldtech.com/repository/CN=Starfield Secure Certification Authority↩
/serialNumber=10688435
   i:/C=US/O=Starfield Technologies, Inc./OU=Starfield Class 2 Certification Authority
-----BEGIN CERTIFICATE-----
MIIFBzCCA++gAwIBAgICAgEwDQYJKoZIhvcNAQEFBQAwaDELMAkGA1UEBhMCVVMx
[...]

Если второй сертификат не тот, что вам нужен, проверьте остальную цепочку; некоторые серверы выдают цепочку в неверном порядке. Если вы не смогли найти сертификат издателя в цепочке, вам придётся поискать его где-нибудь ещё. Один из способов — просмотреть расширение Authority Information Access в конечном сертификате:

$ openssl x509 -in fd.crt -noout -text
[...]
    Authority Information Access:
        OCSP - URI:http://ocsp.starfieldtech.com/
        CA Issuers - URI:http://certificates.starfieldtech.com/repository/sf↩
_intermediate.crt
[...]

Если поле CA Issuers присутствует, в нем должен содержаться URL-адрес сертификата издателя. Если же информация о сертификате издателя недоступна, можно попробовать открыть требуемый сайт в браузере, позволить ему восстановить цепочку, и скачать сертификат издателя из просмотрщика сертификатов браузера. Если всё это не сработало, можно поискать сертификат в своём доверенном хранилище, либо на сайте самого удостоверяющего центра.

Если вам удалось получить все сертификаты, и требуется лишь определить адрес ответчика OCSP, используйте параметр -ocsp_uri с командой x509:

$ openssl x509 -in fd.crt -noout -ocsp_uri
http://ocsp.starfieldtech.com/

Теперь можно отправить OCSP-запрос:

$ openssl ocsp -issuer issuer.crt -cert fd.crt -url http://ocsp.starfieldtech.com/ ↩
-CAfile issuer.crt
WARNING: no nonce in response
Response verify OK
fd.crt: good
        This Update: Feb 18 17:59:10 2013 GMT
        Next Update: Feb 18 23:59:10 2013 GMT

В ответе нужно обратить внимание на две вещи. Во-первых, проверьте, что ответ сам по себе является валидным (строка Response verify OK в предыдущем примере). Во-вторых, проверьте, что говорится в ответе. Если статус указан как good, значит сертификат не был отозван. Для отозванных сертификатов статус будет revoked.

Примечание: Предупреждающее сообщение об отсутствии однократно используемого числа (nonce) говорит о том, что OpenSSL хотел использовать однократное число в качестве защиты от атак повторного воспроизведения (replay attack), но ответ сервера не содержал ни одного такого числа. Обычно подобное происходит потому, что удостоверяющие центры хотят улучшить производительность своих ответчиков OCSP. Когда они отключают защиту с помощью однократно используемых чисел (стандарт позволяет это), ответы OCSP могут быть запрошены (обычно в потоковом режиме), закэшированы, и повторно использованы в течение определённого периода времени.

Вы можете столкнуться с ответчиками OCSP, которые не выдают успешного ответа на приведённый выше запрос. В таких ситуациях могут помочь следующие действия:

Не запрашивать однократное число

Некоторые серверы не могут обработать запрос однократно используемого числа и отвечают сообщением об ошибке. По умолчанию OpenSSL запрашивает это число. Запрос однократно используемого числа предотвращается параметром командной строки -no_nonce.

Предоставить заголовок запроса Host

Хотя большинство серверов OCSP отвечают на HTTP-запросы, в которых не указано корректное имя хоста в заголовке Host, некоторые этого не делают. Если вы столкнулись с сообщением об ошибке, содержащим код ошибки HTTP (например, 404), попробуйте добавить в свой OCSP-запрос имя хоста. Вы можете сделать это при использовании OpenSSL 1.0.0 или более поздней версии с помощью недокументированного параметра -header.

С учётом этих двух соображений итоговая команда будет выглядеть так:

$ openssl ocsp -issuer issuer.crt -cert fd.crt -url http://ocsp.starfieldtech.com/ ↩
-CAfile issuer.crt -no_nonce -header Host ocsp.starfieldtech.com

Тестирование привязки OCSP

Привязка OCSP (OCSP Stapling) — это опциональная функция, позволяющая добавить к сертификату сервера ответ OCSP, который подтверждает его действительность. Поскольку ответ OCSP доставляется по уже существующему соединению, клиенту не нужно извлекать его отдельно.

Привязка OCSP выполняется только в случае запроса её клиентом, который отправляет расширение status_request в запросе протокола рукопожатия. Сервер, поддерживающий привязку OCSP, вернёт ответ OCSP в качестве составной части протокола рукопожатия.

При использовании инструмента s_client привязка OCSP запрашивается с помощью параметра -status:

$ echo | openssl s_client -connect www.feistyduck.com:443 -status

Связанные с OCSP данные будут отображаться в самом начале вывода информации о соединении. К примеру, если сервер не поддерживает привязку, вы увидите в самом начале вывода следующую строку:

CONNECTED(00000003)
OCSP response: no response sent

Если сервер поддерживает привязку, вы увидите в выводе полный ответ OCSP:

OCSP Response Data:
    OCSP Response Status: successful (0x0)
    Response Type: Basic OCSP Response
    Version: 1 (0x0)
    Responder Id: C = US, O = "GeoTrust, Inc.", CN = RapidSSL OCSP-TGV Responder
    Produced At: Jan 22 17:48:55 2014 GMT
    Responses:
    Certificate ID:
      Hash Algorithm: sha1
      Issuer Name Hash: 834F7C75EAC6542FED58B2BD2B15802865301E0E
      Issuer Key Hash: 6B693D6A18424ADD8F026539FD35248678911630
      Serial Number: 0FE760
    Cert Status: good
    This Update: Jan 22 17:48:55 2014 GMT
    Next Update: Jan 29 17:48:55 2014 GMT
[...]

Статус сертификата good означает, что этот сертификат не был отозван.

Проверка статуса отзыва сертификата с помощью CRL

Проверить статус отзыва сертификата с помощью списков отзыва сертификатов (Certificate Revocation List, CRL) даже сложнее, чем через OCSP. Процесс состоит из следующих шагов:

  1. Получить сертификат, который вы хотите проверить на отзыв.

  2. Получить сертификат издателя.

  3. Скачать и проверить подлинность CRL.

  4. Найти в этом CRL серийный номер нужного сертификата.

Первые два шага совпадают с теми, что мы выполняли для проверки статуса с помощью OCSP, инструкции по ним можно найти в соответствующем разделе.

Местонахождение CRL закодировано в сертификате сервера; поищите раздел "X509v3 CRL Distribution Points" в текстовом выводе. Например:

$ openssl x509 -in fd.crt -noout -text | grep -A 5 CRL
[...]
                  URI:http://rapidssl-crl.geotrust.com/crls/rapidssl.crl

Затем получите CRL из удостоверяющего центра:

$ wget http://rapidssl-crl.geotrust.com/crls/rapidssl.crl

Убедитесь, что CRL действителен (то есть подписан сертификатом издателя):

$ openssl crl -in rapidssl.crl -inform DER -CAfile issuer.crt -noout
verify OK

Теперь нужно определить серийный номер сертификата, который вы хотите проверить:

$ openssl x509 -in fd.crt -noout -serial
serial=0FE760

И наконец, можно преобразовать CRL в удобный для чтения человеком формат и проинспектировать его вручную:

$ openssl crl -in rapidssl.crl -inform DER -text -noout
Certificate Revocation List (CRL):
        Version 2 (0x1)
    Signature Algorithm: sha1WithRSAEncryption
        Issuer: /C=US/O=GeoTrust, Inc./CN=RapidSSL CA
        Last Update: Jan 25 11:03:00 2014 GMT
        Next Update: Feb  4 11:03:00 2014 GMT
        CRL extensions:
            X509v3 Authority Key Identifier:
                keyid:6B:69:3D:6A:18:42:4A:DD:8F:02:65:39:FD:35:24:86:78:91:16:30

            X509v3 CRL Number:
                92103
Revoked Certificates:
    Serial Number: 0F38D7
        Revocation Date: Nov 26 20:07:51 2013 GMT
    Serial Number: 6F29
        Revocation Date: Aug 15 20:48:57 2011 GMT
[...]
    Serial Number: 0C184E
        Revocation Date: Jun 13 23:00:12 2013 GMT
    Signature Algorithm: sha1WithRSAEncryption
         95:df:e5:59:bc:95:e8:2f:bb:0a:4f:20:ad:ca:8f:78:16:54:
         35:32:55:b0:c9:be:5b:89:da:ba:ae:67:19:6e:07:23:4d:5f:
         16:18:5c:f3:91:15:da:9e:68:b0:81:da:68:26:a0:33:9d:34:
         2d:5c:84:4b:70:fa:76:27:3a:fc:15:27:e8:4b:3a:6e:2e:1c:
         2c:71:58:15:8e:c2:7a:ac:9f:04:c0:f6:3c:f5:ee:e5:77:10:
         e7:88:83:00:44:c4:75:c4:2b:d3:09:55:b9:46:bf:fd:09:22:
         de:ab:07:64:3b:82:c0:4c:2e:10:9b:ab:dd:d2:cb:0c:a9:b0:
         51:7b:46:98:15:83:97:e5:ed:3d:ea:b9:65:d4:10:05:10:66:
         09:5c:c9:d3:88:c6:fb:28:0e:92:1e:35:b0:e0:25:35:65:b9:
         98:92:c7:fd:e2:c7:cc:e3:b5:48:08:27:1c:e5:fc:7f:31:8f:
         0a:be:b2:62:dd:45:3b:fb:4f:25:62:66:45:34:eb:63:44:43:
         cb:3b:40:77:b3:7f:6c:83:5c:99:4b:93:d9:39:62:48:5d:8c:
         63:e2:a8:26:64:5d:08:e5:c3:08:e2:09:b0:d1:44:7b:92:96:
         aa:45:9f:ed:36:f8:62:60:66:42:1c:ea:e9:9a:06:25:c4:85:
         fc:77:f2:71

В начале CRL присутствуют метаданные, за ними следует список отозванных сертификатов, а в конце находится электронная подпись (которую мы проверяли на предыдущем шаге). Если серийный номер сертификата сервера находится в списке, значит он был отозван.

Если вы не хотите визуально искать нужный серийный номер (некоторые CRL могут быть довольно длинными), используйте команду grep, но будьте внимательны при указании формата номера (например, при необходимости удалите префикс 0x, опустите все нули в начале номера и преобразуйте все буквы в верхний регистр). Например:

$ openssl crl -in rapidssl.crl -inform DER -text -noout | grep FE760

Тестирование повторного согласования параметров соединения

У инструмента s_client есть парочка функций, с помощью которых можно вручную протестировать возможность повторного согласования параметров соединения (renegotiation). Во-первых, при установлении соединения данный инструмент сообщит, поддерживает ли удаленный сервер безопасное повторное согласование. Это возможно потому, что сервер, поддерживающий безопасное повторное согласование, обозначает данный факт с помощью специального расширения TLS, которым обмениваются во время фазы протокола рукопожатия. Если такая поддержка имеется, вывод команды может выглядеть так (представляющая интерес строка выделена жирным шрифтом):

New, TLSv1/SSLv3, Cipher is AES256-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    [...]

Если же безопасное повторное согласование не поддерживается, вывод будет другой:

Secure Renegotiation IS NOT supported

Даже если сервер обозначил поддержку безопасного повторного согласования, у вас может возникнуть вопрос, позволяет ли этот сервер инициировать повторное согласование со стороны клиента. Инициированное клиентом повторное согласование — это не требующаяся на практике функция протокола (поскольку при необходимости сервер всегда может самостоятельно инициировать повторное согласование), которая делает сервер более уязвимым для атак типа "отказ от обслуживания".

Для инициации повторного согласования вам нужно напечатать в отдельной строке букву R. Например (подразумевается, что вы взаимодействуете с HTTP-сервером), вы сначала можете напечатать первую строку запроса, затем инициировать повторное согласование, после чего ввести оставшуюся часть запроса. Вот как это выглядит при взаимодействии с веб-сервером, поддерживающим инициализацию повторного согласования со стороны клиента:

HEAD / HTTP/1.0
R
RENEGOTIATING
depth=3 C = US, O = "VeriSign, Inc.", OU = Class 3 Public Primary Certification ↩
Authority
verify return:1
depth=2 C = US, O = "VeriSign, Inc.", OU = VeriSign Trust Network, OU = "(c) 2006 ↩
VeriSign, Inc. - For authorized use only", CN = VeriSign Class 3 Public Primary ↩
Certification Authority - G5
verify return:1
depth=1 C = US, O = "VeriSign, Inc.", OU = VeriSign Trust Network, OU = Terms of use at ↩
https://www.verisign.com/rpa (c)06, CN = VeriSign Class 3 Extended Validation SSL CA
verify return:1
depth=0 1.3.6.1.4.1.311.60.2.1.3 = US, 1.3.6.1.4.1.311.60.2.1.2 = California, ↩
businessCategory = Private Organization, serialNumber = C2759208, C = US, ST = ↩
California, L = Mountain View, O = Mozilla Corporation, OU = Terms of use at ↩
www.verisign.com/rpa (c)05, OU = Terms of use at www.verisign.com/rpa (c)05, CN = ↩
addons.mozilla.org
verify return:1
Host: addons.mozilla.org

HTTP/1.1 301 MOVED PERMANENTLY
Content-Type: text/html; charset=utf-8
Date: Tue, 05 Jun 2012 16:42:51 GMT
Location: https://addons.mozilla.org/en-US/firefox/
Keep-Alive: timeout=5, max=998
Transfer-Encoding: chunked
Connection: close

read:errno=0

При повторном согласовании сервер вновь посылает свои сертификаты клиенту (в приведённом выводе вы можете увидеть верификацию цепочки сертификатов). После этого продолжается заголовок HTTP-запроса (строка, начинающаяся с Host). Подобный ответ веб-сервера является подтверждением того, что повторное согласование параметров соединения действительно поддерживается. Поскольку в разных версиях библиотек SSL/TLS проблема повторного согласования решается по-разному, серверы, не поддерживающие повторного согласования, могут либо разорвать соединение, либо оставить его открытым, но отказаться от продолжения обмена по нему (что обычно приводит к таймауту).

Пример, когда сервер, не поддерживающий повторное согласование, просто отказывается от второго протокола рукопожатия в рамках соединения:

HEAD / HTTP/1.0
R
RENEGOTIATING
140003560109728:error:1409E0E5:SSL routines:SSL3_WRITE_BYTES:ssl handshake failure:s3↩
_pkt.c:592:

Во время написания данного материала OpenSSL по умолчанию выполнял соединения с серверами, не поддерживающими безопасное повторное согласование; также он поддерживал и безопасное, и небезопасное повторное согласование, выбирая в пользу того, какое из них способен обеспечить сервер. При успешном выполнении повторного согласования параметров соединения с сервером, не поддерживающим безопасное повторное согласование, вы будете знать, что сервер поддерживает небезопасное инициированное клиентом повторное согласование.

Примечание: Самым надёжным способом проверки на поддержку сервером небезопасного повторного согласования является использование метода, описанного в этом разделе, но с версией OpenSSL, выпущенной до того, как возможность небезопасного повторного согласования была обнаружена (например, 0.9.8k). Я упоминаю об этом, потому что существует небольшое количество серверов, которые поддерживают как безопасное, так и небезопасное повторное согласование. Эту уязвимость трудно обнаружить с помощью современных версий OpenSSL, которые отдают предпочтение безопасному варианту.

Тестирование на уязвимость BEAST

Атака BEAST использует уязвимость, существующую во всех версиях протокола SSL, а также TLS до версии 1.1. Эта уязвимость затрагивает все наборы шифров CBC и потоки данных как клиента, так и сервера; однако, атака BEAST работает только против данных на стороне клиента. Большинство современных браузеров используют так называемое разделение 1/n-1 в качестве обходного манёвра для предотвращения данного эксплойта, но некоторые серверы продолжают со своей стороны развертывать средства, уменьшающие вероятность этой атаки, особенно если среди их клиентов много пользователей с более старыми (с неисправленной уязвимостью) браузерами.

Идеальный подход для снижения данной угрозы — полагаться только на TLS 1.1 и выше, но эти новые протоколы ещё недостаточно широко поддерживаются. Ситуация осложняется ещё и тем фактом, что в настоящее время RC4 сам по себе считается небезопасным. Если вы считаете, что BEAST представляет большую угрозу, чем недостатки RC4, можно для работы с современными клиентами использовать TLS 1.2, а для всех остальных принудительно использовать RC4. При этом возможны два подхода:

Строгое ограничение

Не поддерживать какие-либо наборы CBC при использовании TLS 1.0 и более ранних протоколов, оставив задействованными только наборы RC4. Клиенты, не поддерживающие RC4, не смогут установить защищённое соединение. Этот режим исключает некоторых потенциальных пользователей веб-сайта, но эксперты по сертификации PCI DSS требуют внедрения именно такого варианта взаимодействия.

Приоритезация RC4

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

Порядок тестирования на уязвимость BEAST зависит от того, какое поведение вы ожидаете от сервера. При тестировании обоих подходов мы должны убедиться в том, что инструмент OpenSSL s_client на нашей стороне использует только небезопасные протоколы, указав при его вызове параметры -no_ssl2, -no_tls_1_1 и -no_tls_1_2.

Для тестирования сервера на поддержку строгого ограничения попытаемся соединиться с ним, отключив на нашей стороне все наборы RC4:

$  echo | openssl s_client -connect www.feistyduck.com:443 \
-cipher 'ALL:!RC4' -no_ssl2 -no_tls1_1 -no_tls1_2

Если соединение удалось успешно установить (что возможно только при использовании одного из уязвимых наборов CBC), значит строгое ограничение на сервере не настроено.

Для тестирования сервера на поддержку приоритезации RC4 попытаемся соединиться с ним, переместив все наборы RC4 в конец списка набора шифров:

$ echo | openssl s_client -connect www.feistyduck.com:443 \
-cipher 'ALL:+RC4' -no_ssl2 -no_tls1_1 -no_tls1_2

Сервер с приоритезацией RC4 выберет для соединения один из наборов RC4, проигнорировав все наборы CBC, которые также были предложены. Если же вы видите какое-либо иное поведение, значит на сервере не было предпринято никаких попыток уменьшить вероятность атаки BEAST.

Тестирование на уязвимость Heartbleed

Протестировать наличие уязвимости Heartbleed можно либо вручную, либо с помощью одного из доступных инструментов (которых существует великое множество, поскольку выполнить Heartbleed очень легко). Но, как это обычно бывает с подобного рода инструментами, возникает вопрос с правильностью их работы. Имеются данные о том, что некоторые из них не могут обнаружить уязвимые серверы16. Учитывая серьезность Heartbleed, лучше всего провести тестирование вручную или с помощью инструмента, который дает вам полную наглядность процесса. В этом разделе будет описан подход, который можно использовать только с модифицированной версией OpenSSL.

Некоторые части этого теста не требуют модификации OpenSSL при условии, что у вас имеется версия с поддержкой протокола Heartbeat (1.0.1 или новее). Например, для определения того, поддерживает ли удалённый сервер протокол Heartbeat, используйте параметр -tlsextdebug при соединении с сервером, чтобы отобразить поддерживаемые им расширения:

$ openssl s_client -connect www.feistyduck.com:443 -tlsextdebug
CONNECTED(00000003)
TLS server extension "renegotiation info" (id=65281), len=1
0001 - <SPACES/NULS>
TLS server extension "EC point formats" (id=11), len=4
0000 - 03 00 01 02                                       ....
TLS server extension "session ticket" (id=35), len=0
TLS server extension "heartbeat" (id=15), len=1
0000 - 01
[...]

Сервер, который не возвращает расширение heartbeat, не подвержен уязвимости Heartbleed. Чтобы протестировать, отвечает ли сервер на запросы heartbeat, используйте параметр -msg для запроса показа сообщений протокола, а после установки соединения с сервером наберите символ B и нажмите "Ввод":

$ openssl s_client -connect www.feistyduck.com:443 -tlsextdebug -msg
[...]
---
B
HEARTBEATING
>>> TLS 1.2  [length 0025], HeartbeatRequest
    01 00 12 00 00 3c 83 1a 9f 1a 5c 84 aa 86 9e 20
    c7 a2 ac d7 6f f0 c9 63 9b d5 85 bf 9a 47 61 27
    d5 22 4c 70 75
<<< TLS 1.2  [length 0025], HeartbeatResponse
    02 00 12 00 00 3c 83 1a 9f 1a 5c 84 aa 86 9e 20
    c7 a2 ac d7 6f 52 4c ee b3 d8 a1 75 9a 6b bd 74
    f8 60 32 99 1c
read R BLOCK

В данном выводе продемонстрирована полная пара запроса и ответа heartbeat. Второй и третий байты в обоих сообщениях heartbeat определяют длину полезной нагрузки. Мы запросили полезную нагрузку в 18 байтов (шестнадцатеричное 12) и сервер ответил полезной нагрузкой аналогичного размера. В обоих случаях присутствуют также дополнительные 16 байтов заполнителя. Первые два байта в полезной нагрузке составляют порядковый номер, который OpenSSL использует для сопоставления ответов с запросами. Остальные байты полезной нагрузки и заполнитель являются просто случайными данными.

Чтобы выявить уязвимый сервер, нужно подготовить специальную версию OpenSSL, которая будет посылать некорректную длину полезной нагрузки. Уязвимые серверы берут заявленную длину полезной нагрузки и в ответе приводят запрашиваемое количество байтов, независимо от длины фактической полезной нагрузки.

На этом этапе вы должны решить, хотите ли вы создать инвазивный тест (который будет выполнять эксплойт на сервере путем извлечения некоторых данных из процесса) или неинвазивный тест. Всё зависит от того, в каких условиях вы находитесь. Если у вас есть разрешение на проведение подобных тестов, используйте инвазивный тест. С его помощью вы сможете в точности увидеть то, что возвращается, и исключить ошибки. Например, некоторые версии GnuTLS поддерживают Heartbeat и отвечают на запросы с некорректной длиной полезной нагрузки, но они не будут возвращать актуальные данные процесса на сервере. Неинвазивный тест не способен надежно диагностировать такую ситуацию.

Неинвазивную версию теста можно создать с помощью такого патча для OpenSSL 1.0.1h:

--- t1_lib.c.original   2014-07-04 17:29:35.092000000 +0100
+++ t1_lib.c    2014-07-04 17:31:44.528000000 +0100
@@ -2583,6 +2583,7 @@
 #endif

 #ifndef OPENSSL_NO_HEARTBEATS
+#define PAYLOAD_EXTRA 16
 int
 tls1_process_heartbeat(SSL *s)
        {
@@ -2646,7 +2647,7 @@
                 * sequence number */
                n2s(pl, seq);

-               if (payload == 18 && seq == s->tlsext_hb_seq)
+               if ((payload == (18 + PAYLOAD_EXTRA)) && seq == s->tlsext_hb_seq)
                        {
                        s->tlsext_hb_seq++;
                        s->tlsext_hb_pending = 0;
@@ -2705,7 +2706,7 @@
        /* Message Type */
        *p++ = TLS1_HB_REQUEST;
        /* Payload length (18 bytes here) */
-       s2n(payload, p);
+       s2n(payload + PAYLOAD_EXTRA, p);
        /* Sequence number */
        s2n(s->tlsext_hb_seq, p);
        /* 16 random bytes */

Для получения неинвазивного теста мы увеличиваем длину полезной нагрузки на 16 байтов (длина заполнителя). Когда сервер с уязвимостью отвечает на такой запрос, он вернёт заполнитель и ничего больше. Для получения инвазивного теста увеличьте длину полезной нагрузки, скажем, до 32 байтов. Сервер с уязвимостью будет отвечать полезной нагрузкой в 50 байтов (18 байтов, посылаемых OpenSSL по умолчанию, плюс ваши 32 байта), а также отправит 16 байтов заполнителя. Увеличивая таким способом длину полезной нагрузки, мы можем заставить сервер с уязвимостью вернуть до 64 Кбайт данных. Сервер без уязвимости Heartbleed не будет отвечать на некорректные запросы.

Чтобы создать собственный инструмент для тестирования Heartbleed, распакуйте свежую копию исходного кода OpenSSL, отредактируйте файл ssl/t1_lib.c как показано в представленном патче, скомпилируйте как обычно, но не выполняйте инсталляцию. Готовый бинарник openssl будет находиться в поддиректории apps/. Поскольку он скомпилирован статически, его можно переименовать в что-то вроде openssl-heartbleed и переместить в какое-нибудь стандартное расположение исполняемых файлов.

Пример вывода от сервера с уязвимостью, возвращающего 16 байтов серверных данных (выделено жирным шрифтом):

B
HEARTBEATING
>>> TLS 1.2  [length 0025], HeartbeatRequest
    01 00 32 00 00 7c e8 f5 62 35 03 bb 00 34 19 4d
    57 7e f1 e5 90 6e 71 a9 26 85 96 1c c4 2b eb d5
    93 e2 d7 bb 5f
<<< TLS 1.2  [length 0045], HeartbeatResponse
    02 00 32 00 00 7c e8 f5 62 35 03 bb 00 34 19 4d
    57 7e f1 e5 90 6e 71 a9 26 85 96 1c c4 2b eb d5
    93 e2 d7 bb 5f 6f 81 0f aa dc e0 47 62 3f 7e dc
    60 95 c6 ba df c9 f6 9d 2b c8 66 f8 a5 45 64 0b
    d2 f5 3d a9 ad
read R BLOCK

Если вы хотите получить больше данных за один запрос, увеличьте длину полезной нагрузки, перекомпилируйте и запустите тест заново. Либо можно ещё раз получить блок данных такого же размера путём повторного ввода символа B.

Определение стойкости параметров алгоритмов обмена Диффи-Хеллмана

В OpenSSL 1.0.2 и более новых версиях при соединении с сервером команда s_client выводит значение стойкости эфемерного ключа Диффи-Хеллмана, если таковой используется. Таким образом, для того, чтобы определить надежность параметров DH некоторого сервера, вам просто нужно подключиться к нему, предлагая только наборы шифров, использующие обмен ключами DH. Например:

$ openssl-1.0.2 s_client -connnect www.feistyduck.com:443 -cipher kEDH
[...]
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: DH, 2048 bits
---
[...]

Серверы, поддерживающие экспортные наборы шифров, могут фактически предлагать даже более слабые параметры DH. Чтобы проверить такой вариант развития событий, выполните подключение, предлагая при этом только экспортные наборы DHE:

$ openssl-1.0.2 s_client -connnect www.feistyduck.com:443 -cipher kEDH+EXPORT

При подключении к хорошо настроенному серверу такая команда должна завершить выполнение неудачно. В противном случае сервер скорее всего предложит согласовать небезопасные параметры DH с длиной ключа в 512 бит.

Примечания

[15] Сюрпризы при верификации в Apple OpenSSL (Hynek Schlawack, 3 марта 2014 г.)

[16] Ошибки в скриптах нахождения уязвимости Heartbleed (Shannon Simpson и Adrian Hayter, 14 апреля 2014 г.)