Шрифт:
Выявление ошибки на этапе анализа кода
Прежде всего найдите все точки входа в приложение из сети. Для каждой точки входа определите, используется ли протокол SSL. API сильно зависит от библиотеки и языка, но поиска по словам «SSL» и «TLS» без учета регистра обычно хватает. Если вы пользуетесь старыми библиотеками Windows, ищите слово «РСТ» (Private Communication Technology – технология защищенной связи), это устаревшая версия предшественника SSLv3, разработанная Microsoft. Если некоторая точка входа не защищена SSL, может возникнуть серьезная проблема!
Остальные обсуждаемые в этой главе вопросы в большей степени связаны с кодом клиента, так как сервер часто аутентифицирует клиента по паролю или с помощью какого–то другого механизма. Но если используются клиентские сертификаты, то следует применять ту же методологию анализа и к коду сервера.
Для каждой точки входа, защищенной SSL, проверьте, сравнивается ли сертификат со списком известных хороших сертификатов (список разрешенных). В этом случае программа обычно не обращается к коммерческой инфраструктуре PKI, а реализует собственные средства управления сертификатами.
Если сертификат находится в списке допустимых, то все равно остается риск, что он отозван. Не исключено также, что сам этот список формируется небезопасным образом. Так или иначе, проверку следует проводить до начала обмена данными по установленному соединению.
Если программа не обращается к списку допустимых сертификатов, проверьте, выполнены ли все перечисленные ниже проверки:
□ сертификат подписан известным УЦ или имеется цепочка подписей, ведущая к известному УЦ;
□ срок действия сертификата еще не истек;
□ имя хоста сравнивается со значением в соответствующем подполе хотя бы одного из двух полей: DN или subjectAltName (последнее появилось в версии спецификации Х.509 v3);
□ неудачное завершение любой из этих проверок программа рассматривает как ошибку аутентификации и отказывается устанавливать соединение.
Во многих языках программирования для решения этой задачи приходится глубоко забираться в документацию или даже в сам код. Например, может ветретиться такой код на языке Python, в котором используется стандартный модуль «socket», включенный в Python 2.4:
import socket
s = socket.socket
s.connect((\'www.example.org\', 123))
ssl = socket.ssl(s)
Совершенно не ясно, какие именно проверки библиотека SSL выполняет по умолчанию. В случае Python ответ таков: согласно документации, библиотека не проверяет абсолютно ничего. В других языках могут проверяться срок действия и цепочка доверия, но тогда вы должны быть уверены, что имеется приемлемый список УЦ, и предпринять какие–то действия, если это не так.
Анализируя, насколько правильно реализована работа с отозванными сертификатами, посмотрите, используются ли вообще CRL–списки или протокол OCSP. И в этом отношении API сильно различаются, поэтому лучше изучить тот API, который применен в конкретной программе; поиска по словам «CRL» или «OCSP» без учета регистра обычно достаточно.
В случае, когда используются один или оба этих механизма, нужно обращать внимание на следующие вопросы:
□ производится ли проверка до отправки данных;
□ что происходит, если проверка завершается неудачно;
□ как часто загружаются CRL–списки;
□ проверяются ли сами CRL (особенно если они были загружены по обычному протоколу HTTP или LDAP).
Ищите код, который «заглядывает внутрь» сертификата в поисках некоторых деталей, например, значения поля DN, но не выполняет нужных криптографических операций. Так, следующий фрагмент греховен, поскольку проверяет лишь, что в сертификате есть текст «CN=www. example. сот», но ведь кто угодно мог выпустить для себя сертификат с таким именем:
X509Certificate cert = new X509Certificate;
if (cert.Subject == "CN=www.example.com") {
// Ура, мы общаемся с example.com!
}
Тестирование
В настоящее время имеется несколько программ, которые автоматизируют атаку с «человеком посередине» против HTTPS. В частности, к ним относятся dsniff и ethercap. Впрочем, они работают только против HTTPS, поэтому при использовании против совместимого с HTTPS приложения должны отображать какое–нибудь окно или иным способом оповещать об ошибке, поскольку в противном случае под угрозой может оказаться вся инфраструктура приложения.