Дэвид Лебланк - 19 смертных грехов, угрожающих безопасности программ
HANDLE hMutex = CreateMutex(... аргументы ...);
if (hMutex == NULL)
return false;
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
CloseHandle(hMutex);
return false;
}
Дополнительные защитные меры
Старайтесь вообще избегнуть проблемы, создавая временные файлы в области, выделенной конкретному пользователю, а не в общедоступной. Всегда пишите реентерабельный код, даже если программа не является многопоточной. Если кто–то захочет перенести ее на другую платформу, то его задача значительно упростится.
Другие ресурсы
□ «Resource contention can be used against you» by David Wheeler: www–106. ibm.com/developerworks/linux/library/l–sprace.html?ca=dgr–lnxw07RACE
□ RAZOR research topics: http://razor.bindview.com/publish/papers/signals.txt
□ «Delivering Signals for Fun and Profit: Understanding, Exploiting and Preventing Signal Handling Related Vulnerabilities» by Michal Zalewski: www.bindview.com/Services/Razor/Papers/2001/signals.cfm
Резюме
Рекомендуется
□ Пишите код, в котором нет побочных эффектов.
□ Будьте очень внимательны при написании обработчиков сигналов.
Не рекомендуется
□ Не модифицируйте глобальные ресурсы, не захватив предварительно замок.
Стоит подумать
□ О том, чтобы создавать временные файлы в области, выделенной конкретному пользователю, а не в области, доступной всем для записи.
Грех 17. Неаутентифицированный обмен ключами
В чем состоит грех
Да, я хочу защитить сетевой трафик! Конфиденциальность? Целостность сообщений? Отлично! Я буду пользоваться «впишите свое любимое готовое решение». Э, стоп… Необходимо ведь, чтобы у обеих сторон был общий секретный ключ. А как это сделать?
«Знаю! Я возьму другое готовое решение или напишу сам. Что сказано по этому поводу в книге ^Applied Cryptography* («Прикладная криптография»)? Ага, нашел… Применю–ка я протокол обмена ключами Диффи–Хеллмана. А может быть, даже воспользуюсь SSL или TLS».
Примерно так люди и рассуждают, готовясь реализовать какое–нибудь криптографическое решение, но забывая оценить все сопутствующие риски. Проблема в том, что к безопасности процедуры обмена ключами тоже предъявляются определенные требования: обмениваемые ключи необходимо держать в секрете, и, что еще важнее, все сообщения, передаваемые по протоколу, должны быть надежно аутентифицированы. Иными словами, нужно иметь гарантию, что каждая сторона точно знает, кому вручает свой ключ. Поразительно, как часто это условие не выполняется! Аутентификация пользователей, после того как обмен ключами состоялся, обычно не решает проблему.
Подверженные греху языки
Все языки подвержены этому греху.
Как происходит грехопадение
Эксперты по безопасности (включая и авторов) всегда предостерегают программистов от разработки собственных криптографических алгоритмов и протоколов. Обычно они внемлют этому совету. Когда перед разработчиком встает задача обеспечить защиту сетевых соединений и он осознает, что необходимо какое–то соглашение о ключах, то, как правило, применяет SSL или берет протокол, описанный в книге Брюса Шнейера «Прикладная криптография». Но на этом пути расставлено много силков и капканов.
Немало ошибок можно совершить в процедуре инициализации сеанса. Одна из самых распространенных опасностей – это атака с «человеком посередине». Рассмотрим ее на конкретном примере типичного приложения клиент / сервер, в котором клиент или сервер применяет протокол обмена ключами Диффи–Хел–лмана, а затем аутентифицируют друг друга с помощью выработанного ключа. Детали алгоритма Диффи–Хеллмана несущественны. Достаточно знать, что в этой схеме требуется, чтобы две стороны послали друг другу сообщения, основанные на случайно выбранном секрете. Обладая любым секретом и одним из открытых сообщений, можно вычислить третий секрет (первые два были случайно выбраны сторонами). Предполагается, что определить третий секрет, не зная хотя бы одного их первых двух, – очень трудоемкая вычислительная задача. Третий секрет обычно называют ключом, а процесс его выработки – обменом ключами. На первый взгляд, все замечательно, так как, не зная ни одного из исходных секретов, противник не может вычислить ключ.
Но, если не включить в схему дополнительные механизмы защиты, мы столкнемся с серьезной проблемой. Дело в том, что вся схема уязвима для атаки с «человеком посередине». Предположим, что клиент начал сеанс, но вместо сервера ему ответил противник. В протоколе нет способа определить, общается ли клиент с корректным сервером. На практике противник легко может занять место сервера, а затем обменяться с сервером ключами, действуя в качестве посредника при передаче законного трафика.
Корень проблемы в том, что обмен ключами не аутентифицирован. Стойте! Мы же сказали, что собираемся воспользоваться протоколом аутентификации, как только установим защищенное соединение! Мы могли бы взять ключ, выработанный по схеме Диффи–Хеллмана, и с ним организовать протокол проверки пароля. Совсем хорошо будет, если этот протокол реализует взаимную аутентификацию, то есть клиент и сервер аутентифицируют друг друга.
Ах, если бы таким образом можно было решить проблему!
Представьте, что в «середине» протокола аутентификации находится противник, и он не делает ничего, кроме подслушивания. Предположим, что противник не знает пароля и не получает никакой информации о нем из протокола (быть может, мы применяем схему с одноразовым паролем, например S/KEY). Что доказывает протокол аутентификации? Лишь тот факт, что никто не пытался изменить сообщения протокола (то есть сообщения аутентичны). Даже если противник все подслушал, «аутентификация» состоялась.
А что осталось недоказанным? Что аутентичными были сообщения, передаваемые в ходе исполнения протокола обмена ключами! Таким образом, после завершения аутентификации мы по–прежнему пользуемся неаутентифицированным ключом, которым перед этим обменялись с противником. Этот ключ годится для шифрования и дешифрирования всего трафика, так что у нас нет оснований полагать, что сообщения защищены.
Чтобы организовать защищенный сеанс, обе стороны обычно должны удостовериться в «личности» собеседника (хотя в некоторых случаях одна из сторон может быть анонимной). Личность должна быть уже удостоверена к моменту начала исполнения протокола обмена ключами. При этом каждое последующее сообщение посылается с ключом (требование аутентичности сохраняется на все время сеанса, хотя часто мы называем это целостностью сообщений).
Почти никогда не имеет смысла выполнять обмен ключами без аутентификации. Поэтому все современные протоколы аутентификации, предназначенные для использования в сети, одновременно являются протоколами обмена ключами. И никто не конструирует автономный протокол обмена ключами, поскольку аутентификация – это основополагающее требование.
Родственные грехи
Мы взяли для примера протокол Диффи–Хеллмана, но та же проблема присуща и SSL/TLS, поскольку разработчики плохо понимают, что необходимо для адекватной аутентификации. Коль скоро процедуру аутентификации можно скомпрометировать, открывается возможность для атаки с «человеком посередине». Тема аутентификации в применении к протоколу SSL рассматривается в грехе 10.
Отметим еще, что люди, попавшиеся на этой ошибке, обычно строят собственные криптосистемы, сознательно или нет. Надежно защитить трафик при этом, скорее всего, не удастся (см. грех 8).
Где искать ошибку
Согрешить можно в любом приложении, которое выполняет аутентификацию по сети в ситуации, когда для этого требуется установить соединение, защищенное криптографическими методами. Фундаментальная проблема в том, что автор не осознает, что соединение недостаточно аутентифицировано (а иногда не аутентифицировано вовсе).
Выявление ошибки на этапе анализа кода
Вот как мы предлагаем искать признаки этого греха в программе:
1. Выявите те точки сетевых коммуникаций, где защита трафика обязательна (любой вид аутентификации и последующего обеспечения целостности сообщений, а также конфиденциальности, если это существенно для приложения).
2. Если защиты нет, это, очевидно, плохо.
3. Для каждой точки определите, используется ли при организации сеанса какой–нибудь протокол аутентификации. Плохо, если нет.
4. Проверьте, приводит ли протокол аутентификации к выработке ключа. Для этого нужно изучить выходную информацию протокола. Если не приводит, убедитесь, что протокол аутентифицирует данные, выработанные в ходе обмена ключами, и проверяет «личности» сторон способом, не поддающимся подделке. К сожалению, для обычного разработчика это может оказаться трудной задачей, лучше пригласить профессионального криптографа.
5. Если выработан ключ, проверьте, используется ли он для последующей защиты канала. Если нет, создается угроза локальной атаки с перехватом соединения.