Чтение онлайн

ЖАНРЫ

19 смертных грехов, угрожающих безопасности программ

Виега Джон

Шрифт:

□ SSL_RSA_WITH_DES_CBC_SHA

□ SSL_DHE_RSA_WITH_DES_CBC_SHA

□ SSL_DHE_DSS_WITH_DES_CBC_SHA

□ SSL_RSA_EXPORT_WITH_RC4_4 0_MD5

□ SSL_RSA_EXPORT_WITH_DES4 0_CBC_SHA

□ SSL_DHE_RSA_EXPORT_WITH_DES4 0_CBC_SHA

□ SSL_DHE_DSS_EXPORT_WITH_DES4 0_CBC_SHA

Первые два семейства шифров нежелательны из соображений долгосрочной безопасности, но именно они, скорее всего, и будут использованы! Мы рекомендуем выбирать любое из последующих трех семейств, поскольку AES считается самым лучшим из современных криптографических алгоритмов. (Вопроса о выборе алгоритма открытого ключа и кода аутентификации сообщений (MAC) мы здесь не касаемся.) Чтобы принять только три указанных алгоритма, надо написать такой код:

...

private void useSaneCiperSuites(SSLSocket s) {

s.setEnabledCipherSuites({"TLS_RSA_WITH_AES_128_CBC_SHA",

"TLS_DHE_RSA_WITH_AES_128_CBC_SHA",

"TLS_DHE_DSS_WITH_AES_128_CBC_SHA"});

}

Проверка

сертификата

Разные API в разной степени поддерживают базовую проверку сертификата. Некоторые по умолчанию проверяют дату и цепочку доверия, в других вообще не реализовано ни то, ни другое. Большинство же находятся где–то посередине, например включают средства проверки, но не выполняют ее по умолчанию.

Обычно (хотя и не всегда) для выполнения проверки нужно получить ссылку на сертификат сервера (часто его называют сертификатом «партнера» (peer certificate)). Например, в Java до инициализации SSL–соединения можно зарегистрировать объект–слушатель HandShakeCompletedListener для объекта SSLSocket. Слушатель должен реализовать такой метод:

...

public void handshakeCompleted(HandShakeCompletedEvent event);

Получив объект, описывающий событие, вы можете далее написать:

...

event.getPeerCertificates ;

В результате будет возвращен массив объектов типа javasecuritycert.Certi–ficate. Certificate – это базовый класс, фактический тип полученных объектов обычно представлен производным классом java.security.cert.X509Extension, хотя иногда встречаются и устаревшие сертификаты (типа java.security.cert.X509, которому наследует X509Extension).

Первым в массиве идет сертификат партнера, а за ним – сертификаты удостоверяющих центров по цепочке вплоть до корневого. При вызове этого метода Java API выполняет некоторые проверки сертификатов с целью убедиться в поддержке выбранного семейства шифров, но цепочка доверия не контролируется. Выбрав такой подход, вы должны самостоятельно произвести все проверки, используя открытый ключ (п+1)-го сертификата для контроля n–го, а дойдя до корневого сертификата, сравнить его со списком известных корневых УЦ. (В Java есть и другие способы проверки сертификатов, но они не менее сложны.) Например, чтобы проверить сертификат партнера, когда уже установлено, что вторым в массиве идет доверенный сертификат, нужно сделать следующее:

...

try {

((X509Extension)(certificate[0])).verify(certificate[1].getPublicKey);

} catch (Exception e) {

/* Проверка сертификата завершилась неудачно. */

}

Отметим, что здесь не проверяется корректность даты каждого сертификата. Это можно было бы сделать так:

...

try {

((X509Extension)(certificate[0])).checkValidity;

} catch (Exception e) {

/* Проверка сертификата завершилась неудачно. */

}

В каркасе .NET имеются аналогичные средства:

...

X509Certificate2 cert = new X509Certificate2(@"c:\certs\server.cer");

X509Chain chain = new X509Chain;

chain.Build(cert);

if (chain.ChainStatus.Length > 0) {

// Были ошибки

}

Проверка имени хоста

Предпочтительный способ проверить имя хоста – воспользоваться полем dnsName из расширения subjectAltName, если оно имеется и заполнено. Но часто имя хоста записывается в поле DN. API

для проверки этих полей варьируются в широких пределах.

В JavaJSSE в предположении, что мы имеем дело с сертификатом X509Exten–sion, можно следующим образом проверить значение subjectAltName, а в случае неудачи обратиться к полю DN:

private Boolean validateHost(X509Extension cert) {

...

String s = "";

String EXPECTED_HOST = "www.example.com";

try {

/* 2.5.29.17 – это OID, стандартное числовое представление имени

расширения */

s = new String(cert.getExtensions("2.5.29.17"));

if (s.equals(EXPECTED_HOST)) {

return true;

}

else { /* если расширение есть, но не соответствует

* ожидаемому значению, не будем проверять поле DN,

* которое НЕ ДОЛЖНО иметь другое значение. */

return false;

}

} catch (Exception e) {} /* Такого расширения нет, проверим DN */

if (cert.getSubjectDN.getName.equals(EXPECTED_HOST)) {

return true;

} else {

return false;

}

}

В каркасе .NET имя хоста проверяется автоматически при вызове метода SslStream. AuthenticateAsClient.

Проверка отзыва сертификата

Самым популярным способом проверки факта отзыва сертификата (если вообще можно говорить о популярности столь нечасто применяемой методики) по–прежнему остается сверка с CRL–списком. Следовало бы рекомендовать протокол OCSP, но УЦ не торопятся с его поддержкой. Компания VeriSign поддерживает его, пожалуй, лучше других, она готова отвечать на запрос о статусе каждого когда–либо выпущенного ей сертификата (включая также сертификаты, выпущенные компаниями RSA и Thawte). Ее сервер находится по адресу(если вы пользуетесь библиотекой, поддерживающей протокол OCSP).

Но обратимся к CRL–спискам. Во–первых, для сверки вы должны иметь много CRL–списков. Необходимо узнать адрес точки распространения CRL, которая (если существует) может быть доступна по протоколам HTTP или LDAP. Иногда адрес указан в сертификате, а иногда – нет. В табл. 10.1 приведен список известных точек распространения CRL, работающих по протоколу HTTP. Можете использовать этот список в случае, когда адрес отсутствует в самом сертификате.

Во–вторых, нужно решить, как часто загружать CRL–списки. Обычно УЦ регулярно обновляют списки отозванных сертификатов, даже если никаких новых записей в них не появилось. Мы рекомендуем загружать новую версию с точно такой же периодичностью, не позже чем через 24 ч после обновления.

В–третьих, необходимо контролировать, что загруженный CRL–список действительно опубликован соответствующим УЦ (для этого нужно проверить цифровую подпись).

И наконец, проверяя каждый предъявленный сертификат, следует убедиться, что ни один из сертификатов в цепочке доверия не внесен в имеющиеся CRL–списки. Если какой–то сертификат отозван, то соединение устанавливать нельзя.

В CRL заносятся просто идентификаторы сертификатов. Чтобы сверить сертификат с CRL–списком, нужно извлечь поле ID и посмотреть, есть ли оно в этом списке.

Таблица 10.1. Адреса точек распространения CRL–списков для популярных УЦ

Дополнительные защитные меры

В идеале, помимо описанных в этой главе проверок, надо бы проверять и другие критические расширения сертификата Х.509. При этом вы должны ясно понимать смысл всех критических расширений. Тогда вы не спутаете, например, сертификат для подписания кода с SSL–сертификатом. Вообще говоря, такие проверки могут представлять интерес, но обычно они не настолько важны, как может показаться.

Поделиться с друзьями: