Криптопереписка для недоверчивых

от автора

Осторожно: данный пост может вызывать непродолжительное обострение паранойи

Привет! Не верите ли вы в популярные продукты для защищённой переписки так, как не верю в них я? Например, в браузерные крипточаты с шифрованием на стороне клиента, или в p2p-криптомессенжеры?

В данном посте речь пойдет об организации защищённого общения между двумя собеседниками. Он адресован таким же недоверчивым людям как я, поэтому в нём не будет ни кода, написанного мной, ни изобретённых на коленке протоколов и алгоритмов. Будет использоваться только библиотека openssl и набор программ openssh.

image

Вступление

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

К сожалению, люди совершают ошибки. Взять, например, сервис, первый по ссылке гугла по запросам «secure chat» и «crypto chat», который называется Cryptocat. Примерно три месяца назад в его коде обнаружилась ошибка — из-за недостаточно хорошей реализации генератора случайных чисел групповые сообщения, которые передавались в период с 17 октября 2011 года по 15 июня 2013 года стало возможно расшифровать, см. Decryptocat. Причём, в начале 2013 года компанией Veracode проводился аудит кода, который присудил ей «Security Quality Score of 100/100», и это был не единственный аудит кода в этот период.

Поэтому возникла идея использовать для коммуникации библиотеки, открытые алгоритмы и протоколы с более чем десятилетней историей поиска уязвимостей. Для меня, как и, надеюсь, что для многих из вас — это алгоритмы и протоколы из openssl и openssh(который, кстати, использует openssl внутри), а именно: TLS, SSH, SHA1, AES256, RSA и D-H. Я буду рассказывать на их примере, но, конечно, вы можете заменить их на те, которым доверяете лично вы, основная идея при этом не изменится.

Об OpenSSL

OpenSSL — библиотека с открытым исходным кодом, реализующая протоколы SSL/TLS. Они используются каждый раз, когда вы заходите на сайт по HTTPS. Про это недавно была статья.

Вот список уязвимостей, которые нашли в 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, rfc 4346 и rfc 5246. Забавно, что все они заканчиваются на 46. Мы будем использовать версию 1.0 т.к. она вышла в 1999 году и в ней не было найдено серьёзных уязвимостей.

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

Помимо полной реализации TLS, библиотека OpenSSL содержит множество криптографических и математических алгоритмов и имеет консольный интерфейс.

Примеры использования

1) Тестирование чисел на простоту:

$ 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, ключ для шифрования согласуется с помощью алгоритма Диффи-Хеллмана(D-H). Вот отличное пятиминутное видео, объясняющее его суть. Алгоритм D-H позволяет получить общий секретный ключ, используя незащищённый от прослушивания канал связи.

На этом можно было бы и закончить, но у такого подхода есть три проблемы:
1) Он не защищает от атаки «человек посередине»(MITM, Man In The Middle). Идея атаки тривиальна — становимся между сервером и клиентом, клиент думает, что подключается к серверу, а на самом деле, подключается к нам(к злоумышленнику), а мы транслируем его трафик на настоящий сервер.
2) Требуется наличие белого ip хотя бы у одной стороны.
3) Даже если данные нельзя расшифровать, доступна «метаинформация» о соединении: кто с кем соединялся, когда и сколько данных передано.

Защищаемся от MITM

Для того, чтобы защититься MITM, нужно, чтобы сервер аутентифицировал клиента, а клиент — сервера. Это достигается использованием асимитричного шифрования.

На на сервере и на клиенте нужно сгенерировать закрытый RSA-ключ и публичный сертификат.

На сервере выполняем:

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 спешит на помощь

Протокол 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. Она позволяет слушать и устанавливать tcp-соединения, а так же передавать по ним данные. При выполнении команд выше, стороннему наблюдателю будет казаться, что никакого туннеля нет и что это просто два ssh-подключения к одному и тому же серверу, никак не связанных между собой. Таким образом, получаем защищённое и относительно незаметное соединение.

Что плохо? То, что мы не можем доверять ssh-серверу! Теоретически он может писать расшифрованные данные к себе в лог в момент их перешифровки, когда они попадают из одного туннеля в другой.

SSH + OpenSSL или We have to go deeper

Теперь пустим наш 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 к любому серверу.

MiniFAQ

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 обязательно должно резолвиться.

ссылка на оригинал статьи http://habrahabr.ru/post/192184/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *