Поиск по сайту:

Тестирование клиентов HTTPS с использованием openssl для имитации сервера


В этой статье описывается, как протестировать ваш HTTPS-клиент или браузер с помощью openssl. Чтобы протестировать ваш HTTPS-клиент, вам понадобится HTTPS-сервер или веб-сервер, например IIS, apache, nginx или openssl. Вам также понадобятся несколько тестовых примеров. В SSL/TLS существует три распространенных режима сбоя:

  1. Клиент устанавливает соединение, когда этого не должно быть,

  2. Соединение терпит неудачу, когда оно должно быть успешным, и

  3. Соединение установлено правильно, но данные при передаче повреждены.

  4. Существует четвертый режим отказа: данные могут быть переданы небезопасно. Этот режим отказа выходит за рамки данной статьи.

Чтобы убедиться, что любые проблемы, обнаруженные в ходе тестирования, связаны с проблемами в вашем HTTPS-клиенте, мы хотим использовать «известный исправный» HTTPS-сервер. Нам также нужен «педантичный» или «неумолимый» сервер. openssl точно соответствует этим требованиям.

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

ЗНАЕТЕ ЛИ ВЫ?
«Клиент» — это компьютер или компьютерная программа, которая инициирует соединение с «сервером». «Сервер» — это компьютерная программа, которая ожидает подключения от «клиента». Для HTTP и HTTPS существуют «браузеры» и «клиенты». Браузеры предназначены для взаимодействия с людьми и обычно имеют графический интерфейс пользователя. Все браузеры являются клиентами HTTP/HTTPS.

Однако существуют клиенты HTTP/HTTPS, которые не являются браузерами. Эти клиенты предназначены для использования в качестве автоматизированных систем. Мудрый разработчик сервера позаботится о том, чтобы его система могла эффективно использоваться с клиентами HTTPS, которые являются браузерами, и клиентами HTTPS, которые не являются браузерами.

В этом уроке вы узнаете:

  • Как выбрать хороший HTTPS-клиент или браузер

  • Как использовать openssl в качестве HTTPS-сервера

  • Как использовать HTTPS-сервер для тестирования HTTPS-клиента

Как протестировать ваш HTTPS-клиент, пошаговые инструкции

Я буду использовать прилагательные «праведный», чтобы указать, что тест выполнил что-то правильно, и «ошибочный», чтобы указать, что тест сделал что-то неправильно. Если тест не пройден, когда должен, то это заслуженный провал. Если тест проходит, хотя не должен, это ошибочное прохождение.

Я хотел использовать HTTPS-клиент, который можно было бы сломать и восстановить по своему желанию, и я нашел его: команду http (она находится на github как httpie). Если я использую опцию -verify=no, то клиент сломан: он будет ошибочно проходить тесты. Мне не удалось создать ошибочный сбой, и это хорошо, поскольку означает, что если клиент дает сбой, значит, что-то не так.

В основе протокола SSL/TLS (поменяли имя и еще немного) лежат два файла: «сертификат» (или для краткости «сертификат») и секретный «ключ». По всему протоколу один конец соединения запрашивает сертификат у другого конца. Первый конец будет использовать часть информации из сертификата для создания математической головоломки, ответить на которую сможет только тот, у кого есть секретный ключ. Секретный ключ никогда не покидает свою машину: решение проблемы означает, что ближний конец знает, что у дальнего конца есть ключ, но не знает, что это за ключ.

Команда openssl по сути представляет собой интерфейс командной строки для libssl. Он содержит примитивный сервер, вызываемый с помощью подкоманды s_server. openssl потребуется пара открытый сертификат/закрытый ключ. В моем случае они уже были у меня на рабочем веб-сервере. Я получил их бесплатно от Let’s Encrypt.

В качестве доказательства того, что сервер работает правильно, я скопировал сертификат и ключ на свою машину разработки и запустил HTTPS-сервер openssl.

На стороне сервера:

$ openssl s_server -status_verbose -HTTP -cert fullchain.pem -key privkey.pem
Using default temp DH parameters
ACCEPT

Моя первая попытка провалилась!

$ http --verify=yes jeffs-desktop:4433/index.html

http: error: ConnectionError: (Connection aborted., RemoteDisconnected(Remote end closed connection without response)) while doing GET request to URL: http://jeffs-desktop:4433/index.html

Первая гипотеза: ключ и сертификат не совпадают. Я проверил это:

$ openssl x509 -noout -modulus -in fullchain.pemls  | openssl md5
(stdin)= b9dbd040d9a0c3b5d3d50af46bc87784
$ openssl rsa -noout -modulus -in privkey.pem | openssl md5
(stdin)= b9dbd040d9a0c3b5d3d50af46bc87784

Они совпадают. Так почему же это терпит неудачу? Поскольку мой сертификат предназначен для linuxconfig.dns.net, но в качестве имени хоста я использую jeffs-desktop.

jeffs@jeffs-desktop:~/documents$ openssl x509 -text -noout -in fullchain.pem | fgrep CN
        Issuer: C = US, O = Let’s Encrypt, CN = R3
        Subject: CN = linuxconfig.ddns.net

Это оправданный сбой: сервер был неправильно настроен, и мой клиент это обнаружил. Если бы я использовал -verify=no, тогда у меня будет сломанный клиент, и он не обнаружит проблему. Обратите внимание, что любые передаваемые данные по-прежнему будут защищены от перехвата. Я могу решить эту проблему, изменив свой файл /etc/hosts, указав свои собственные адреса IPv4 и IPv6.

192.168.1.149   linuxconfig.ddns.net
2601:602:8500:b65:155a:7b81:65c:21fa    linuxconfig.ddns.net

(кстати, простота, с которой вы можете подделать IP-адрес, в первую очередь является одной из причин использования SSL/TLS). Попробуйте еще раз. На стороне сервера:

$ openssl s_server -status_verbose -HTTP -cert fullchain.pem -key privkey.pem
Using default temp DH parameters
ACCEPT

На стороне клиента:

http --verify=yes https://linuxconfig.ddns.net:4433/index.html
On the server side, I get the error message:

140101997737280:error:14094418:SSL routines:ssl3_read_bytes:tlsv1 alert unknown ca:../ssl/record/rec_layer_s3.c:1543:SSL alert number 48
On the client side, I get the error message:
http: error: SSLError: HTTPSConnectionPool(host='linuxconfig.ddns.net', port=4433): Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1131)'))) while doing GET request to URL: https://linuxconfig.ddns.net:4433/

Это сообщение об ошибке CERTIFICATE_VERIFY_FAILED является важной подсказкой: оно означает, что центр сертификации (CA) сертификата не удалось проверить. Поскольку клиент не мог проверить сертификат, если не удалось установить соединение. Это еще одна праведная неудача.

Сам сертификат может быть подделан, и клиент не сможет об этом узнать. Однако сертификат ссылается на центр сертификации (ЦС), и ЦС либо знает, что сертификат действителен, либо отклоняет проверку. Как мы узнаем, что центр сертификации заслуживает доверия?

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

$ openssl s_client -showcerts -connect linuxconfig.ddns.net:4433
CONNECTED(00000003)
depth=0 CN = linuxconfigan.ddns.net
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN = linuxconfigan.ddns.net
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
 0 s:CN = linuxconfigan.ddns.net
   i:C = US, O = Let's Encrypt, CN = R3
-----BEGIN CERTIFICATE-----

Я знаю, что мой производственный сервер работает правильно. Вот как должна выглядеть цепочка (обратите внимание на номер порта 443, а не 4433):

$ openssl s_client -showcerts -connect linuxconfig.ddns.net:443
CONNECTED(00000003)
depth=2 C = US, O = Internet Security Research Group, CN = ISRG Root X1
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = R3
verify return:1
depth=0 CN = linuxconfig.ddns.net
verify return:1
---
Certificate chain
 0 s:CN = linuxconfig.ddns.net
   i:C = US, O = Let's Encrypt, CN = R3
-----BEGIN CERTIFICATE-----
MIIFYjCCBEqgAwIBAgISA0MTOSmISSsIyRls8O/2XpAaMA0GCSqGSIb3DQEBCwUA
...
-----END CERTIFICATE-----
 1 s:C = US, O = Let's Encrypt, CN = R3
   i:C = US, O = Internet Security Research Group, CN = ISRG Root X1
-----BEGIN CERTIFICATE----
...
-----END CERTIFICATE-----
 2 s:C = US, O = Internet Security Research Group, CN = ISRG Root X1
   i:O = Digital Signature Trust Co., CN = DST Root CA X3
-----BEGIN CERTIFICATE-----
…

Здесь есть два пути: я могу отключить проверку сертификата или добавить сертификат Let’s Encrypt в список известных центров сертификации. Отключить проверку можно быстро и безопасно. Добавление ЦС в список известных ЦС является более загадочной задачей. Давайте сделаем и то, и другое. На стороне сервера я ничего не трогал. На стороне клиента отключаю проверку и получаю:

$ http –verify=no  https://linuxconfig.ddns.net:4433/index.html
http: error: ConnectionError: ('Connection aborted.', BadStatusLine('\n')) while doing GET request to URL: https://linuxconfig.ddns.net:4433/index.html
$ echo $?
1

Это сообщение об ошибке сообщает мне, что произошло нарушение протокола HTTP (не HTTPS). Сервер обработал первую строку файла index.html, хотя он должен был вернуть блок заголовка возврата HTTP. Это ошибка на стороне сервера, и она нарушит работу всех HTTP-клиентов. Внимательный просмотр документации подсказывает мне использовать опцию -WWW (не -www) с openssl вместо опции -HTTP. Я делаю это:

openssl s_server -status_verbose -WWW -cert fullchain.pem -key privkey.pem и он работает правильно, с оговоркой, что проверка сертификата у меня еще не заработала.

$ http -verify=no https://linuxconfig.ddns.net:4433/helloworld.c
HTTP/1.0 200 ok
Content-type: text/plain

#include 
int main (int argc, char *argv[]) {
        printf("Hello, world\n\n");
}

Поскольку я использовал -verify=no, это на самом деле ошибочный проход.

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

$ openssl verify -purpose sslserver fullchain.pem
CN = linuxconfig.ddns.net
error 20 at 0 depth lookup: unable to get local issuer certificate
error cert.pem: verification failed

Быстрое решение заключалось в том, чтобы попробовать команду openssl s_server на моем рабочем веб-сервере, используя файлы рабочей конфигурации. Это (достаточно) безопасно, поскольку сервер openssl будет работать через порт 4433, а мой рабочий сервер — через порт 443.

# openssl s_server -status_verbose -WWW \
-cert /etc/letsencrypt/live/linuxconfig.ddns.net/fullchain.pem  \
-key /etc/letsencrypt/live/linuxconfig.ddns.net/privkey.pem -accept 4433

Хм. Nginx работает как чемпион. openssl нет. Вот почему openssl является лучшим испытательным стендом, чем nginx: если конфигурация nginx неверна, он попытается разобраться. Если конфигурация openssl неправильная, он вам позвонит. Конфигурация openssl хранится в /etc/ssl/openssl.cnf.

Там сказано, что сертификаты CA находятся в /etc/ssl/certs . Корневой сертификат Internet Services Research Group (ISRG) находится там. Но давайте зашифруем промежуточный сертификат — нет. В каком-то смысле это имеет смысл: у Let's Encrypt есть замечательный certbot, который знал все о nginx, когда я его запускал, но я не запускал certbot с openssl, поэтому сертификата Let's Encrypt не было в /etc/ssl/ сертификаты/ . Я получил сертификат Let's Encrypt с:

$ wget https://letsencrypt.org/certs/lets-encrypt-r3.pem

Приведенная выше команда скопировала файл lets_encrypt_r3.pem в /etc/ssl/certs/, запустила программу c_rehash и вуаля:

# openssl verify -CApath /etc/ssl/certs/ \ 	/etc/letsencrypt/live/linuxconfig.ddns.net/fullchain.pem
/etc/letsencrypt/live/linuxconfig.ddns.net/fullchain.pem: OK

Это хорошо, но вопрос в том, могу ли я увидеть helloworld.c?

$ http --verify=yes https://linuxconfig.ddns.net:4433/helloworld.c
HTTP/1.0 200 ok
Content-type: text/plain

#include 
int main (int argc, char *argv[]) {
	printf("Hello, world\n\n");
}

Да. Теперь я убедился, что мой работающий HTTPS-клиент справедливо пройдет и справедливо потерпит неудачу, по крайней мере, в тестовых случаях, с которыми я работал. Есть и другие проблемы с SSL/TLS, например списки отзыва сертификатов (CRL), но я надеюсь, что вы поняли это правильно.

Далее я хочу убедиться, что файлы, передаваемые между HTTPS-сервером openssl и моим HTTPS-клиентом, не будут повреждены ни на грамм. Я не могу убедиться, что каждый файл будет передан без ошибок, но я могу передать большой двоичный файл, убедиться, что он был передан правильно, а затем сделать вывод, что большие файлы не будут повреждены.

Я использовал команду ls -lorS, чтобы найти большой файл, вычислил его сумму SHA256, передал его, используя openssl в качестве сервера, сохранил полученный файл и вычислил сумму SHA256 для этого файла. Суммы SHA 256 должны совпадать.

На стороне сервера:

$ ls -lorS | tail -1
-rw-rw-r-- 1 jeffs 121329853 May 23  2020 CybersecurityEssentials.pdf
$ sha256sum CybersecurityEssentials.pdf
49a49c8e525a3d6830fce1c1ee0bfce2d3dd4b000eeff5925b074802e62024e0 CybersecurityEssentials.pdf

На стороне клиента:

$ http --verify=no https://linuxconfig.ddns.net:4433/CybersecurityEssentials.pdf -o /tmp/CybersecurityEssentials.pdf 
$ sha256sum /tmp/CybersecurityEssentials.pdf 
49a49c8e525a3d6830fce1c1ee0bfce2d3dd4b000eeff5925b074802e62024e0  /tmp/CybersecurityEssentials.pdf

Размер этого PDF-файла составляет 121 МБ, этого достаточно для моих целей. Суммы SHA256 совпадают, поэтому файл был передан правильно.

Заключение

В этой статье я описал распространенные режимы сбоя протокола HTTPS. Я использовал некоторые критерии для выбора HTTPS-сервера для тестирования HTTPS-клиента и выбрал openssl. Я выбрал простой в использовании HTTPS-клиент. Я показал некоторые распространенные режимы сбоев и заметил, что клиент обнаружил эти сбои.

Сложнее всего было правильно настроить openssl, поэтому я показал, что может пойти не так и как это исправить. Наконец, я продемонстрировал, что, используя openssl в качестве сервера и моего HTTPS-клиента, я могу передавать файл без повреждения данных.

Статьи по данной тематике: