KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программирование » Дэвид Лебланк - 19 смертных грехов, угрожающих безопасности программ

Дэвид Лебланк - 19 смертных грехов, угрожающих безопасности программ

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Дэвид Лебланк, "19 смертных грехов, угрожающих безопасности программ" бесплатно, без регистрации.
Перейти на страницу:

Искупление греха

Как и во многих других случаях, первым шагом к искуплению греха должно стать уяснение сути проблемы. Затем посмотрите, актуальна ли эта проблема для вашего приложения. Если вы дочитали до этого места, то, по крайней мере, понимаете, насколько ненадежной может быть информация, возвращаемая DNS–cep–вером.

В отличие от многих других грехов, мы не можем привести конкретные детали, однако упомянем ряд полезных инструментов. Один из самых простых подходов заключается в том, чтобы защищать все соединения по протоколу SSL. Если речь идет о программах, работающих в пределах компании, то имеет смысл установить корпоративный сервер сертификатов и выпустить сертификаты для всех клиентских систем.

Другой вариант – воспользоваться протоколом IPSec. Если IPSec работает поверх Kerberos, то часть работы по аутентификации клиентов и серверов за вас уже проделана; есть уверенность, что любая система, соединившаяся с вашей, как минимум, находится в той же области Kerberos (в терминологии Windows, домене или лесу). IPSec на основе сертификатов тоже работает неплохо, хотя для корректного конфигурирования и эксплуатации инфраструктуры открытых ключей (PKI) потребуется приложить некоторые усилия. Недостатком всех решений на базе IPSec является то, что информация о структуре сети недоступна прикладному уровню, стало быть, ваше приложение отдано на милость сетевому администратору. Есть еще один способ: потребовать наличия IPSec–защищенного участка сети между вашей системой и DNS–сервером. Тогда, по крайней мере, есть гарантия, что вы общаетесь именно с вашим DNS–сервером, поэтому степень доверия к разрешению внутренних имен повышается. Обратите внимание: мы НЕ сказали, что проблема решена, она лишь несколько утратила остроту.

Если аутентификация производится через Kerberos или с помощью внутреннего механизма Windows, причем установлены последние версии клиентов и серверов, то протокол препятствует атакам с «человеком посередине». Впрочем, взлом паролей по–прежнему возможен.

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

Дешевый и малопривлекательный способ решения проблемы состоит в том, чтобы вообще отказаться от применения DNS и отобразить доменные имена на IP–адреса с помощью файла hosts. Если вас беспокоит возможность атаки на локальный сетевой уровень, то избежать подлога ARP–записей можно, сделав их все статическими. Но усилия, неизбежные при таком администрировании, редко оправдываются, разве что вы специально хотите изолировать некоторые машины от основной сети.

Другие ресурсы

□ Building Internet Firewalls, Second Edition by Elizabeth D. Zwicky, Simon Cooper and D. Brent Chapman (O'Reilly, 2000)

□ OzEmail: http://members.ozemail.com.au/~987654321/impact_of_rfc_on_ dns_spoofing.pdf

Резюме

Рекомендуется

□ Применяйте криптографические методы для идентификации клиентов и серверов. Проще всего использовать для этой цели SSL.

Не рекомендуется

□ Не доверяйте информации, полученной от DNS–сервера, она ненадежна!

Стоит подумать

□ Об организации защиты по протоколу IPSec тех систем, на которых работает ваше приложение.

Грех 16. Гонки

В чем состоит грех

Гонка (race condition), по определению, может возникнуть, когда есть две программы, выполняемые в разных контекстах (процессах или потоках). Эти программы могут прерывать друг друга, и при этом каждая изменяет один и тот же ресурс. Если вы думаете, что некоторая короткая последовательность команд или системных вызовов обязательно выполняется атомарно и не может быть прервана другим потоком либо процессом, то совершаете типичную ошибку. Даже имея неопровержимые доказательства существования ошибки, многие программисты склонны ее недооценивать. Но ведь на практике многие системные вызовы выполняют тысячи (иногда миллионы) команд, поэтому сплошь и рядом не успевают завершиться в течение кванта времени, отведенного текущему процессу или потоку.

Мы не будем вдаваться в детали, но сообщим, что простая гонка в многопоточной «ping–звонилке» как–то вывела из строя сервис–провайдера Интернет почти на сутки. Неправильно реализованная блокировка ресурса привела к тому, что приложение посылало ping–запросы на один и тот же IP–адрес с очень высокой скоростью. Знать о существовании гонок важно потому, что чаще всего они проявляются на самых быстродействующих процессорах, прежде всего в системах с двумя процессорами. Это аргумент в пользу того, чтобы руководство обеспечило всех разработчиков быстрыми двухпроцессорными машинами!

Подверженные греху языки

Как и во многих других случаях, гонка может возникнуть в программе, написанной на любом языке. Языки высокого уровня, не поддерживающие потоков и разветвления процессов, не подвержены некоторым видам гонок, но сравнительно низкая производительность таких языков делает их уязвимыми для атак, основанных на разнице во времени между моментом проверки и моментом использования ресурса (time of check to time of use – TOCTOU).

Как происходит грехопадение

Основная ошибка, которая приводит к возникновению гонки, – это программирование с побочными эффектами, против чего предостерегают все учебники. Если функция не реентерабельна и два потока одновременно исполняют ее, то рано или поздно произойдет ошибка. Как вы теперь уже, наверное, понимаете, почти любая программная ошибка при некотором невезении и достаточных усилиях, приложенных противником, может быть превращена в эксплойт. Вот иллюстрация на С++:

...

list<unsigned long> g_TheList;

unsigned long getNextFromList()

{

unsigned long ret = 0;

if (!g_TheList.empty())

{

ret = g_TheList.front();

g_TheList.pop_front();

}

return ret;

}

Возможно, вы надеетесь, что шансы на то, что два потока одновременно окажутся в этой функции, малы. Однако немногие приведенные выше предложения на С++ транслируются в тысячи машинных команд. Достаточно, чтобы один поток закончил проверку наличия элементов в списке, перед тем как другой извлечет из списка последний элемент с помощью вызова pop_front. Как говорил Клинт Иствуд в фильме «Грязный Гарри»: «Ну и как ты себя теперь чувствуешь?» Очень похожий код как раз и привел к тому, что провайдер чуть ли не сутки отказывал в обслуживании клиентам.

Другое проявление проблемы – это возникновение гонки при обработке сигналов. Впервые ЭТс1 Э.ТЭ.КЭ. была публично описана в статье Михала Залевски «Delivering Signals for Fun and Profit: Understanding, Exploiting and Preventing Signal Handling Related Vulnerabilities» («Доставка сигналов для забавы и к собственной выгоде: описание, эксплуатация и предотвращение уязвимостей, связанных с обработкой сигналов»). Ее текст можно найти на странице h.org/files/4/signals.txt. Проблема в том, что многие приложения для UNIX не готовы к ситуациям, встречающимся в многопоточных программах. Ведь даже параллельные приложения, написанные для UNIX и UNIX–подобных систем, обычно порождают новый процесс, а после изменения какой–нибудь глобальной переменной этот процесс получает собственную копию страницы памяти (это гарантируется семантикой копирования при записи). Многие программы далее реализуют обработчики сигналов, причем иногда один обработчик ассоциируется с несколькими сигналами. Приложение спокойно занимается своей работой, как вдруг противник посылает ему пару сигналов, разделенных очень коротким промежутком времени. И вот внезапно ваше приложение становится по сути дела многопоточным! Довольно трудно писать многопоточные программы, даже если знаешь, к чему готовиться; что уж говорить о том, когда такой «подлости» не ожидаешь.

Целый класс ошибок касается взаимодействий с файлами и другими объектами. Способов попасть в беду так много, что даже не перечесть. Вот лишь несколько примеров. Ваша программа должна создать временный файл, поэтому она сначала проверяет, нет ли уже файла с тем же именем, и если нет, то создает его.

Обычное дело, не правда ли? Так–то оно так, но этой сценарий открывает возможность для атаки – противник угадывает имя временного файла и после запуска вашей программы создает ссылку на что–нибудь важное. Вашей программе не повезло, она открывает ссылку, которая ведет на файл, выбранный противником, после чего некоторые ее действия могут привести к эскалации привилегий. Если вы удалите файл, противник сможет подставить вместо него вредоносную программу. Если вы затрете файл, то в дальнейшем это может стать причиной сбоя в какой–то другой программе. Если файл доступен непривилегированным процессам, то вы можете изменить его права и предоставить противнику право записи в какой–то конфиденциальный файл. При наихудшем развитии событий ваша программа может установить для файла режим запуска от имени пользователя root (setuid root), и в результате приложение, выбранное противником, получит административные привилегии.

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*