- PVSM.RU - https://www.pvsm.ru -
Осторожно: данный пост может вызывать непродолжительное обострение паранойи
Привет! Не верите ли вы в популярные продукты для защищённой переписки так, как не верю в них я? Например, в браузерные крипточаты с шифрованием на стороне клиента, или в p2p-криптомессенжеры?
В данном посте речь пойдет об организации защищённого общения между двумя собеседниками. Он адресован таким же недоверчивым людям как я, поэтому в нём не будет ни кода, написанного мной, ни изобретённых на коленке протоколов и алгоритмов. Будет использоваться только библиотека openssl и набор программ openssh.

Во всех продуктах защищённой коммуникации, которые я встречал, у меня неизбежно возникал вопрос доверия их разработчикам. Я не понимал, почему, скажем, к автору продукта всё ещё не пришли и не попросили ослабить шифрование или отдавать клиенту с определённым ip-адресом особый javascript-код. Вы можете сказать: «это невозможно». Почитайте, например, недавний пост Почтовый сервис Lavabit вынужден закрыться [1], и, особенно, продолжение этой истории [2](англ.), прошедшее мимо хабра.
К сожалению, люди совершают ошибки. Взять, например, сервис, первый по ссылке гугла по запросам «secure chat» и «crypto chat», который называется Cryptocat [3]. Примерно три месяца назад в его коде обнаружилась ошибка — из-за недостаточно хорошей реализации генератора случайных чисел групповые сообщения, которые передавались в период с 17 октября 2011 года по 15 июня 2013 года стало возможно расшифровать, см. Decryptocat [4]. Причём, в начале 2013 года компанией Veracode [5] проводился аудит кода [6], который присудил ей «Security Quality Score of 100/100», и это был не единственный [7] аудит кода в этот период.
Поэтому возникла идея использовать для коммуникации библиотеки, открытые алгоритмы и протоколы с более чем десятилетней историей поиска уязвимостей. Для меня, как и, надеюсь, что для многих из вас — это алгоритмы и протоколы из openssl [8] и openssh [9](который, кстати, использует openssl внутри), а именно: TLS, SSH, SHA1, AES256, RSA и D-H. Я буду рассказывать на их примере, но, конечно, вы можете заменить их на те, которым доверяете лично вы, основная идея при этом не изменится.
OpenSSL — библиотека с открытым исходным кодом, реализующая протоколы SSL/TLS. Они используются каждый раз, когда вы заходите на сайт по HTTPS. Про это недавно была статья [10].
Вот список уязвимостей [11], которые нашли в OpenSSL за более чем 10 лет анализа.
Неполный список программ, которые используют OpenSSL: apache, nginx, squid, openvpn, openssh, ntp, dhcp, cups, syslog-ng, xorg-server, php, python, ruby, libevent, nodejs, curl, wget, links, lynx, socat, hostapd, wpa_supplicant, virtualbox, vmware-player, libreoffice, ffmpeg.
И совсем немного про TLS. TLS — это криптопротокол, который позволяет устанавливать защищённые соединения. Сейчас существуют 3 версии этого протокола: 1.0, 1.1 и 1.2. Они описаны, соответственно, в rfc 2246 [12], rfc 4346 [13] и rfc 5246 [14]. Забавно, что все они заканчиваются на 46. Мы будем использовать версию 1.0 т.к. она вышла в 1999 году и в ней не было найдено серьёзных уязвимостей.
Протокол очень хитрый. Очень упрощённо процесс выглядит так: стороны договариваются о ключе для симметричного шифрования и в дальнейшем его используют. Все сообщения защищаются от изменения/подмены при помощи сочетания хэш функции с секретным ключём(HMAC [15]). Так же, в протоколе предусмотрена взаимная аутентификация сервера и клиента
Помимо полной реализации TLS, библиотека OpenSSL содержит множество криптографических и математических алгоритмов и имеет консольный интерфейс.
$ openssl prime 997
3E5 is prime
2) Генерация случайных чисел:
$ openssl rand -hex 16
2871002e6f3cff937d066da9d3017197
3) Вычисление контрольной суммы:
$ openssl sha1 /etc/passwd
SHA1(/etc/passwd)= 8b74a9f1ddf496c02bc85e0e120bbb903d922276
4) Шифрование:
$ openssl aes-256-cbc -k pass -in /etc/passwd
<много бинарного вывода>
Именно этот интерфейс мы и будем использовать для нашей задачи. Перейдём к делу.
Первый собеседник (с «белым» ip, назовём его «сервер») выполняет:
openssl s_server -accept 4433 -nocert -cipher ADH-AES256-SHA -tls1 -no_ticket
Второй собеседник, клиент, выполняет:
openssl s_client -connect <host>:4433 -cipher ADH-AES256-SHA -tls1 -no_ticket
Что мы только что сделали? Первой командой мы запустили tls 1.0-сервер, а второй — подключились к нему. Теперь можно общаться, все сообщения шифруются алгоритмом AES256 [16], ключ для шифрования согласуется с помощью алгоритма Диффи-Хеллмана [17](D-H). Вот отличное пятиминутное видео [18], объясняющее его суть. Алгоритм D-H позволяет получить общий секретный ключ, используя незащищённый от прослушивания канал связи.
На этом можно было бы и закончить, но у такого подхода есть три проблемы:
1) Он не защищает от атаки «человек посередине» [19](MITM, Man In The Middle). Идея атаки тривиальна — становимся между сервером и клиентом, клиент думает, что подключается к серверу, а на самом деле, подключается к нам(к злоумышленнику), а мы транслируем его трафик на настоящий сервер.
2) Требуется наличие белого ip хотя бы у одной стороны.
3) Даже если данные нельзя расшифровать, доступна «метаинформация» о соединении: кто с кем соединялся, когда и сколько данных передано.
Для того, чтобы защититься MITM, нужно, чтобы сервер аутентифицировал клиента, а клиент — сервера. Это достигается использованием асимитричного шифрования [20].
На на сервере и на клиенте нужно сгенерировать закрытый RSA [21]-ключ и публичный сертификат.
На сервере выполняем:
openssl genrsa -out server.key 2048
openssl req -new -key server.key -batch -days 3650 -x509 -out server.crt
На клиенте:
openssl genrsa -out client.key 2048
openssl req -new -key client.key -batch -days 3650 -x509 -out client.crt
Здесь, 2048 — количество бит в модуле закрытого ключа, 3650 — срок действия сертификата в днях.
Затем сервер должен передать свой сертификат(файл server.crt) клиенту, а клиент(файл client.crt) — серверу. Это слабое место, ведь злоумышленник может подменить сертификат в момент передачи(если он его просто прочитает, то ничего страшного). Поэтому, лучше передавать его, используя несколько каналов(pastebin, email, skype, jabber, sms, голосом по телефону, почтой России, стеганографией в фотографиях котят в блоге, и.т.д). Передать сертификат достаточно один раз.
После этого, команда для создания TLS-сервера будет выглядеть так:
openssl s_server -accept 4433 -cert server.crt -key server.key -Verify 0 -CAfile client.crt -cipher DHE-RSA-AES256-SHA -tls1 -no_ticket
Подключение к серверу:
openssl s_client -connect <host>:4433 -cert client.crt -key client.key -verify 0 -CAfile server.crt -cipher DHE-RSA-AES256-SHA -tls1 -no_ticket
Теперь сервер и клиент смогут аутентифицировать друг друга.
Важно: если аутентификация прошла не успешно, то ни сервер, ни клиент не разорвёт соединение! Они лишь напишут об ошибке. К сожалению, нет ключа, который бы менял это поведение, поэтому нужно внимательно смотреть в лог подключения. Ошибка выглядит примерно так:
verify error:num=18:self signed certificate
Будем считать, что проблему MITM мы решили, осталось понять как решить проблемы 2) и 3). Здесь нам поможет протокол SSH.
Протокол SSH активно используется при администрировании UNIX-серверов. Он чем-то похож на TLS, передаваемые данные тоже шифруются и подписываются. О ключе для шифрования договариваются обе стороны, например, используя тот же алгоритм D-H.
Для того, чтобы осуществить наш коварный план по защищённому обмену информацией, нам понадобится сервер с доступом по SSH и с белым IP. Найти такой сервер в наши дни не составляет особой сложности. Лучше искать тот, к которому подключаются много и часто.
Одна из особенностей протокола — он позволяет создавать защищённые туннели. Этим мы и воспользуемся!
На сервере выполняем:
ssh -c aes256-cbc -m hmac-sha1-96 -o KexAlgorithms=diffie-hellman-group-exchange-sha1 -R 4433:127.0.0.1:4433 user@sshhost
nc -l -p 4433 -v
Здесь, для дополнительного спокойствия, мы явно указываем алгоритмы шифрования, обмена ключами и хэширования. Ключ -R значит, что все подключения на порт 4433 ssh-сервера пойдут в шифрованный ssh-туннель и попадут к ssh-клиенту на тот же самый порт. Если у пользователя установлен в качестве шелла /sbin/nologin, то иногда помогает использование ключа -N, который указывает ssh-клиенту, не исполнять никаких команд на сервере, а лишь только создать туннель.
На клиенте выполняем:
ssh -c aes256-cbc -m hmac-sha1-96 -o KexAlgorithms=diffie-hellman-group-exchange-sha1 -L 4433:127.0.0.1:4433 user@sshhost
nc 127.0.0.1 4433
Теперь, если клиент подключится к себе на 127.0.0.1 на порт 4433, то ssh-клиент запроксирует соединение по защищённому туннелю на порт 4433 удалённой машины, а оттуда, данные перешифруются и полетят по другому туннелю на машину-сервер.
Команда nc — это вызов популярной утилиты netcat [22]. Она позволяет слушать и устанавливать tcp-соединения, а так же передавать по ним данные. При выполнении команд выше, стороннему наблюдателю будет казаться, что никакого туннеля нет и что это просто два ssh-подключения к одному и тому же серверу, никак не связанных между собой. Таким образом, получаем защищённое и относительно незаметное соединение.
Что плохо? То, что мы не можем доверять ssh-серверу! Теоретически он может писать расшифрованные данные к себе в лог в момент их перешифровки, когда они попадают из одного туннеля в другой.
Теперь пустим наш TLS-трафик поверх цепочки SSH-туннелей. Если вы внимательно следили, то итоговая последовательность команд будет такой:
На сервере:
# следующие три строки обязательны только для соединения в первый раз, в дальнейшем - опциональны
openssl genrsa -out server.key 2048
openssl req -new -key server.key -batch -days 3650 -x509 -out server.crt
[передаём server.crt клиенту]
ssh -c aes256-cbc -m hmac-sha1-96 -o KexAlgorithms=diffie-hellman-group-exchange-sha1 -R 4433:127.0.0.1:4433 user@sshhost
openssl s_server -accept 4433 -cert server.crt -key server.key -Verify 0 -CAfile client.crt -cipher DHE-RSA-AES256-SHA -tls1 -no_ticket
[ждём соединения]
[внимательно смотрим на лог подключения на предмет ошибок валидации сертификата]
[общаемся]
На клиенте:
# следующие три строки обязательны только для соединения в первый раз, в дальнейшем - опциональны
openssl genrsa -out client.key 2048
openssl req -new -key client.key -batch -days 3650 -x509 -out client.crt
[передаём client.crt серверу]
ssh -c aes256-cbc -m hmac-sha1-96 -o KexAlgorithms=diffie-hellman-group-exchange-sha1 -L 4433:127.0.0.1:4433 user@sshhost
openssl s_client -connect localhost:4433 -cert client.crt -key client.key -verify 0 -CAfile server.crt -cipher DHE-RSA-AES256-SHA -tls1 -no_ticket
[внимательно смотрим на лог подключения на предмет ошибок валидации сертификата]
[общаемся]
Для того, чтобы ssh-клиент уходил в background после подключения, ему можно указать ключи -Nf.
Итак, не написав ни одной строчки кода и используя только стандартные инструменты, мы получили возможность переписываться, используя незаметное, способное работать за NAT'ом, соединение, защищённое от прослушивания, MITM-атак и подмены сообщений. К тому же, оно относительно просто для настройки — от каждого собеседника требуется выполнить всего по 4 команды и одну передачу файла для первого соединения, и по две команды для последующих. Да, ещё требуется доступ по SSH к любому серверу.
Q: Почему не VPN?
A: VPN — хорошее решение. Но он сложен в настройке, требует рутовых прав на сервере или доверия к серверу.
Q: Почему не GnuPG?
A: GnuPG — тоже хорошее решение. Но при желании переданные ранее данные можно расшифровать, получив закрытый ключ(например, вместе с ноутбуком).
Q: С чего вы взяли, что в OpenSSL нет уязвимостей?
A: Я уверен, что они там есть. OpenSSL лично мне субъективно кажется надёжным, поэтому я рассказывал на его примере. Можно использовать любую другую реализацию, которой доверяете лично вы.
Q: Обещаете ли вы, что команды правильные?
A: Не обещаю! Советую проверить!
Q: Как дела с кроссплатформенностью?
A: OpenSSL — кроссплатформенная. Клиенты SSH существуют под большинство ОС.
Q: Хочу веб-интерфейс, почему консоль?!
A: Для вэб-интерфейса требуется писать код. Одна небольшая XSS-уязвимость может стоить важных данных. Моя логика проста: нет кода — нет ошибок.
Q: Ошибка getaddrinfo: Name or service not known при выполнении команды openssl s_server
A: У вас, наверно, выключен IPv6. Обновите openssl.
Q: Ошибка gethostbyname failure при выполнении команды openssl s_server
A: Проверьте файл /etc/hosts. Первое имя для адреса 127.0.0.1 обязательно должно резолвиться.
Автор: alexbers
Источник [23]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/ssh/42340
Ссылки в тексте:
[1] Почтовый сервис Lavabit вынужден закрыться: http://habrahabr.ru/post/189510/
[2] продолжение этой истории: http://reason.com/blog/2013/08/18/feds-threaten-to-arrest-email-provider-f
[3] Cryptocat: http://en.wikipedia.org/wiki/Cryptocat
[4] Decryptocat: http://tobtu.com/decryptocat.php
[5] Veracode: http://en.wikipedia.org/wiki/Veracode
[6] аудит кода: https://blog.crypto.cat/2013/02/cryptocat-passes-security-audit-with-flying-colors/
[7] не единственный: https://blog.crypto.cat/2012/11/security-update-our-first-full-audit/
[8] openssl: http://www.openssl.org/
[9] openssh: http://www.openssh.org/
[10] статья: http://habrahabr.ru/post/191954/
[11] список уязвимостей: http://www.openssl.org/news/vulnerabilities.html
[12] rfc 2246: http://tools.ietf.org/html/rfc2246
[13] rfc 4346: http://tools.ietf.org/html/rfc4346
[14] rfc 5246: http://tools.ietf.org/html/rfc5246
[15] HMAC: http://ru.wikipedia.org/wiki/HMAC
[16] AES256: http://ru.wikipedia.org/wiki/Advanced_Encryption_Standard
[17] Диффи-Хеллмана: http://ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B8%D1%84%D1%84%D0%B8_%E2%80%94_%D0%A5%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0
[18] пятиминутное видео: http://habrahabr.ru/post/151599/
[19] «человек посередине»: http://ru.wikipedia.org/wiki/%D0%A7%D0%B5%D0%BB%D0%BE%D0%B2%D0%B5%D0%BA_%D0%BF%D0%BE%D1%81%D0%B5%D1%80%D0%B5%D0%B4%D0%B8%D0%BD%D0%B5
[20] асимитричного шифрования: http://ru.wikipedia.org/wiki/%D0%9A%D1%80%D0%B8%D0%BF%D1%82%D0%BE%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0_%D1%81_%D0%BE%D1%82%D0%BA%D1%80%D1%8B%D1%82%D1%8B%D0%BC_%D0%BA%D0%BB%D1%8E%D1%87%D0%BE%D0%BC
[21] RSA: http://ru.wikipedia.org/wiki/RSA
[22] netcat: http://ru.wikipedia.org/wiki/Netcat
[23] Источник: http://habrahabr.ru/post/192184/
Нажмите здесь для печати.