Дэвид Лебланк - 19 смертных грехов, угрожающих безопасности программ
Чтобы протестировать, как проверяются отозванные сертификаты по CRL–спискам и OSCP, можно просто проанализировать весь исходящий от приложения трафик за достаточно длительный период времени, сравнивая протоколы и адреса получателей с известными значениями. Если проверка по OSCP производится, то на каждую аутентификацию должен приходиться один запрос по этому протоколу. Если ведется проверка по CRL–спискам и она правильно реализована, то списки должны загружаться периодически, скажем, раз в неделю. Поэтому не удивляйтесь, если в коде проверка по CRL–списку присутствует, а в реальном трафике ее следов не видно. Очень может статься, что список уже был загружен и сохранен на локальном компьютере, чтобы избежать лишнего запроса.
Примеры из реальной жизни
Любопытно, что, несмотря на чрезвычайно широкое распространение этого греха (в тот или иной момент от этой проблемы страдали по меньшей мере 90% приложений, в которых использовался SSL, но не HTTPS), во время работы над книгой в базе данных CVE (http://cve.mitre.org) не было ни одного сообщения на эту тему. Нет их и в других аналогичных базах. В CVE обычно заносятся сведения об уязвимостях в популярных приложениях, а этот грех больше присущ специализированным программам, существующим в единственном экземпляре. Тем не менее примеры у нас имеются.
Почтовые клиенты
Протоколы отправки и приема электронной почты уже довольно давно поддерживают SSL–расширения. Они существуют для протоколов РОРЗ, IMAP и SMTP. Когда такой протокол установлен, клиент регистрируется как обычно, но все это происходит в SSL–защищенном туннеле. Разумеется, перед входом в туннель клиент должен аутентифицировать сервера.
Сразу после появления этих протоколов многие почтовые клиенты не реали–зовывали проверку сертификатов вовсе. Те же, которые что–то делали, не проверяли имя хоста, открывая возможность для атаки. И по сей день большинство клиентов не поддерживают ни CRL–списки, ни протокол OCSP (даже в виде необязательной опции).
Когда в 2001 году появилась операционная система Mac OS X, входивший в нее почтовый клиент вообще не поддерживал SSL. Поддержка была добавлена в следующем году, в версии 10.1, но программа была уязвима для обсуждавшихся выше атак. И только в версии 10.3 авторы наконец осознали необходимость тщательной аутентификации серверных сертификатов (в том числе и проверки полей DN и subjectAltName).
Web–браузер Safari
В протокол HTTPS встроен более тщательный контроль сертификатов, чем предполагается по умолчанию в SSL, и прежде всего потому, что этого требуют спецификации протокола. Если быть точным, HTTPS обязан проверять попадание текущей даты в период действия сертификата, прослеживать всю цепочку доверия от сертификата до корневого УЦ и сравнивать имя хоста с записанными в сертификате данными (хотя допускаются и некоторые исключения).
Но Web – очень динамичное место. Принимая во внимание такие вещи, как перенаправление и JavaScript, браузер не всегда может понять истинные намерения отображаемой страницы. Обычно проверяется имя хоста в окончательном URL, а это оставляет возможность обмануть браузер. Например, при покупке авиабилета на сайте united.com вас молча перенаправят на сайт itn.net, и проверяться будет SSL–сертификат именно этого сервера, причем никакого окна с предупреждением вы не получите.
Поэтому для достоверного контроля при работе с браузером пользователь должен щелкать по иконке с замком и просматривать сертификат глазами, дабы убедиться, что имя хоста в сертификате соответствует имени того хоста, на который пользователь хотел попасть. Пользователю предлагается самостоятельно убедиться в том, что itn.net – это правильный сервер. По этой и ряду других причин человеческие ошибки неизбежны (люди зачастую не разрывают соединение, даже увидев предупреждение).
Но браузер Safari от компании Apple не оставляет человеку шансов принять неправильное решение, так как вообще не позволяет ему взглянуть на сертификат! В большинстве других браузеров при щелчке по иконке с замком открывается окно с сертификатом. Но только не в Safari. Последствия могут быть неприятными, в частности упрощается атака «заманивания» (phishing) на хакерский сайт.
Согласно заявлению Apple, решение не показывать сертификат принято сознательно, чтобы не вводить в заблуждение пользователей. Компания полагает, что почти всегда, когда появляется окно с предупреждением, человек его все равно игнорирует, так зачем отвлекать его от работы, предъявляя какой–то загадочный сертификат□
Но вообще–то Apple надо было бы сделать лишь одно: при щелчке по замку открыть окно, в котором показывается хранящееся в сертификате имя хоста (и если заведомо известно, что оно отличается от запрошенного, выделить имя хоста, на который пользователь хотел попасть). Детали сертификата не так важны, хотя для удовлетворения особо любознательных можно было бы добавить кнопку «Подробнее».
SSL–прокси Stunnel
Предположим, что у вас есть очень удобная почтовая программа, с которой вы хотели бы работать безопасно, но вот беда – она не поддерживает SSL. В таком случае вы можете направить ее на SSL–прокси Stunnel, работающий на той же машине, и сконфигурировать прокси так, чтобы он реализовывал всю функциональность протокола SSL. В идеале вы таким образом получите защищенное соединение.
К сожалению, Stimnel далек от идеала. По умолчанию он ничего не проверяет. Если контроль желателен, то у вас есть следующие возможности: необязательная проверка (то есть проверка–то производится, но если завершается с ошибкой, то соединение все равно устанавливается – идея, не слишком удачная), проверка по списку допустимых сертификатов (неплохо, но не всегда достаточно) либо проверка даты и цепочки доверия без анализа важных полей сертификата.
Тем самым Stimnel с точки зрения заявленных целей почти бесполезен!
Искупление греха
Когда использование SSL или TLS оправдано, проверяйте выполнение следующих условий:
□ используется последняя версия протокола (во время работы над книгой это была версия TLS 1.1);
□ используются стойкие шифры (к нестойким относятся прежде всего RC4 и DES);
□ проверяется, что текущая дата попадает в период действия сертификата;
□ гарантируется, что сертификат прямо или косвенно выпущен доверенным источником (корневым УЦ);
□ проверяется, что хранящееся в сертификате имя хоста соответствует ожидаемому.
Кроме того, необходимо реализовать хотя бы один метод работы с отозванными сертификатами: либо сверку с CRL–списком, либо запрос по протоколу OCSP.
Выбор версии протокола
В большинстве языков высокого уровня не существует простого способа указать, каким протоколом вы хотели бы воспользоваться. Вы просто запрашиваете защищенный сокет, а библиотека устанавливает соединение и возвращает результат. Например, в языке Python имеется функция ssl() из модуля socket, которая принимает объект сокета и защищает его по протоколу SSL, но не позволяет указать ни версию протокола, ни шифр. Базовым API в таких языках, как Perl или РНР, присуща та же проблема. Для исправления ситуации часто приходится писать код на языке низкого уровня или копаться в скрытом API, обертывающем такой код (написанный, например, с использованием библиотеки OpenSSL).
В языках низкого уровня возможность задать версию протокола встречается чаще. Так, Java хотя и не поддерживает TLS 1.1 (в версии 1.4.2), но, по крайней мере, позволяет сказать, что вас устраивает только протокол TLS версии 1.0:
...from javax.net.ssl import SSLSocket;
SSLSocket s = new SSLSocket("www.example.com", 25);
s.setEnabledProtocols("TLSv1");
Каркас .NET Framework 2.0 также поддерживает лишь TLS vl.O. В приведенном ниже фрагменте показано, как можно запросить использование TLS. При этом также затребуется проверка даты, а задание в качестве последнего аргумента метода AuthenticateAsClient равным true говорит, что нужно еще проверять сертификат по CRL–списку:
...RemoteCertificateValidationCallback rcvc = new
RemoteCertificateValidationCallback(OnCertificateValidation);
SslStream sslStream = new SslStream(client.GetStream(), false, rcvc);
sslStream.AuthenticateAsClient("www.example.com", // Имя сервера
null, // цепочка сертификатов
SslProtocols.Tls, // использовать TLS
true); // проверять по CRL
// Обратный вызов для дополнительных проверок сертификата
private static bool OnCertificateValidation(object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors) {
if (sslPolicyErrors == SslPolicyErrors.None) {
return false;
}
else {
return true;
}
}
В обоих примерах клиент не сможет установить соединение с сервером, поддерживающим только более старые версии протокола.
В написанных на С библиотеках, например OpenSSL или Microsoft Security Support Provider Interface (SSPI – интерфейс провайдера поддержки безопасности) есть схожие интерфейсы, но, так как это низкоуровневые средства, то кода придется писать больше.