19 смертных грехов, угрожающих безопасности программ
Шрифт:
Но Web – очень динамичное место. Принимая во внимание такие вещи, как перенаправление и JavaScript, браузер не всегда может понять истинные намерения отображаемой страницы. Обычно проверяется имя хоста в окончательном URL, а это оставляет возможность обмануть браузер. Например, при покупке авиабилета на сайте united.com вас молча перенаправят на сайт itn.net, и проверяться будет SSL–сертификат именно этого сервера, причем никакого окна с предупреждением вы не получите.
Поэтому для достоверного контроля при работе с браузером пользователь должен щелкать по иконке с замком и просматривать сертификат глазами, дабы убедиться, что имя хоста в сертификате соответствует имени того
Но браузер 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 – интерфейс провайдера поддержки безопасности) есть схожие интерфейсы, но, так как это низкоуровневые средства, то кода придется писать больше.
Выбор семейства шифров
Как и в случае выбора протокола, задать семейство шифров в языке высокого уровня сложно. В низкоуровневых языках такая возможность есть, но умолчания, на наш взгляд, оставляют желать лучшего. Например, в API Java Secure Sockets Extensions QSSE – защищенные сокеты в Java) в качестве симметричных шифров можно выбирать RC4, DES, 3DES и AES. Но первыми двумя лучше не пользоваться.
Вот полный перечень шифров, предлагаемых Sun, в порядке приоритета (в таком порядке Java будет пробовать их, если вы ничего не укажете):
□ SSL_RSA_WITH_RC4_12 8_MD5
□ SSL_RSA_WITH_RC4_12 8_SHA
□ TLS_RSA_WITH_AES_12 8_CBC_SHA
□ TLS_DHE_RSA_WITH_AES_12 8_CBC_SHA
□ TLS DHE DSS WITH AES 128 CBC SHA
□ SSL_RSA_WITH_3DES_EDE_CBC_SHA
□ SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA
□ SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA