19 смертных грехов, угрожающих безопасности программ
Шрифт:
Все эти ошибки характерны и для баз данных, в которых есть собственные средства управления доступом. Тщательно продумывайте, кому должно быть разрешено читать и модифицировать данные.
Отметим, что в системах, поддерживающих ACL, обычно не стоит применять записи АСЕ, запрещающие доступ к объектам. Предположим, например, что АСЕ содержит такие АСЕ:
Guests: Deny All
Administrators: Allow All
Users: Allow Read
Это будет работать до тех пор, пока кто–то не поместит администратора в группу Guests (что вряд ли имеет смысл). Теперь у администратора нет доступа к ресурсу, поскольку запись, запрещающая доступ, обрабатывается раньше всех записей, которые разрешают доступ.
Еще одна специфичная для Windows проблема заключается в том, что при построении маркера доступа в него сначала включаются группы из домена пользователя, затем группы из домена, в который входит система, на которой пользователь регистрируется, а далее группы, определенные для локальной системы. Неприятность может произойти в случае, когда вы делегируете доступ к ресурсу, находящемуся на другой системе. Если просто взять АСЕ объекта (примером может служить объект Active Directory) и выполнить локальную проверку доступа, то вы можете дать лишние права, если при обработке АСЕ не отбросите локальные группы, например Administrators. Быть может, такое развитие событий кажется вам надуманным, но, к сожалению, это распространенная ошибка, поскольку в некоторых сетях делегирование через Kerberos разрешено. Когда–то похожая проблема была в сервере Microsoft Exchange.
Встраивание секретных данных в код
Следующий грех – «зашивание» секретных данных в ход – вызывает у нас особую досаду. Рассмотрим пример. Ваше приложение должно соединиться с сервером базы данных, для чего нужен пароль, или обратиться к защищенной разделяемой сетевой папке (без пароля и тут не обойтись), или шифровать и дешифрировать данные на лету, пользуясь симметричным ключом. Как это сделать? Простейший и наихудший (читай: самый небезопасный) способ – «зашить» секретные данные (пароль или ключ) в текст программы.
Есть еще одна причина воздерживаться от этого греха, и она никак не связана с безопасностью. Как насчет сопровождения? Представьте, что приложение написано, скажем, на С++, и с ним работают 1200 пользователей. Приложение греховно, в него встроен ключ шифрования, используемый для доступа к серверам. Кто–то раскрыл ключ (это нетрудно, как мы скоро покажем), следовательно, нужно обновить приложение у всех 1200 пользователей. Причем никакой альтернативы вы им не оставляете, так как секретный ключ раскрыт, следовательно, серверы должны быть обновлены, а значит, и все пользователи должны получить новую версию НЕМЕДЛЕННО!
Родственные грехи
С этим грехом тесно связаны «гонки» (race condition), когда неправильно спроектированный механизм управления доступом делает возможными атаки на временные зависимости. Детально эта тема разобрана в грехе 16. Еще несколько грехов касаются обработки данных, поступающих из не заслуживающего доверия источника. Сюда же примыкает проблема некорректного применения криптографии. Иногда последствия раскрытия информации можно сгладить за счет шифрования. Если вы вынуждены хранить информацию в месте, где она может быть модифицирована, то хотя бы снабжайте ее цифровой подписью, чтобы обнаружить попытки манипулирования.
Еще один родственный грех – пренебрежение принципом наименьших привилегий. Если процесс работает от имени root или LocalSystem, то даже самый лучший механизм управления доступом не защитит операционную систему от ваших ошибок – приложению разрешено все, никакой контроль его не остановит.
Где искать ошибку
Чтобы обнаружить ошибки
управления доступом, ищите в коде места, где:□ устанавливаются элементы управления доступом;
□ разрешение на запись дается низко привилегированным пользователям; или
□ создается объект, без явного задания прав доступа к нему;
□ этот объект создается в месте, к которому имеют разрешение на запись низкопривилегированные пользователи;
или
□ конфигурационные данные записываются в разделяемую область памяти; или
□ секретная информация сохраняется в области, которую разрешено читать низкопривилегированным пользователям.
Чтобы найти грех «зашивания», проанализируйте те места программы, где производится шифрование или создаются исходящие аутентифицированные соединения, и определите, откуда берется пароль или ключ. Если он «зашит» в код, имеет место ошибка, которую необходимо исправить (см. следующий раздел).
Выявление ошибки на этапе анализа кода
С точки зрения управления доступом все довольно просто: ищите места, где задаются права на доступ к объекту. Тщательно анализируйте те участки кода, где устанавливаются элементы управления доступом или разрешения. Далее проверьте те места, где создаются файлы или другие объекты без явного задания прав доступа к ним. Спросите себя, достаточно ли устанавливаемых по умолчанию прав с учетом места, где создается объект, и уровня секретности информации.
В поисках греха «зашивания» автор этой главы любит искать некоторые ключевые слова, позволяющие заподозрить код в греховности. Вот эти слова:
□ Secret;
□ Private (разумеется, вы получите много ложных срабатываний от закрытых членов класса);
□ Password;
□ Pwd;
□ Key;
□ Passphrase;
□ Crypt;
□ Cipher и cypher (так тоже пишут!).
Обнаружив любое из этих слов, посмотрите, не связаны ли с ним какие–то «зашитые» в код секретные данные.Тестирование
Инсталлируйте приложение и проверьте, какие элементы управления доступом заданы для созданных объектов. А еще лучше подключиться к функциям, которые создают объекты, и запротоколировать задаваемые права (если приложение предоставляет такую возможность). Тем самым вы сможете увидеть несохраняемые объекты, например временные файлы, события и участки разделяемой памяти.
Что касается «зашитых» секретов, то проще всего проанализировать двоичный файл с помощью, например, такого инструмента, как strings (www.sysinternals.com/ ntw2k/source/misc.shtml#strings). Эта программа выводит все текстовые строки, встречающиеся в приложении, и смотрит, нет ли среди них чего–то похожего на пароль или набор случайных символов (быть может, это ключ?).
На рис. 12.1 приведен результат работы для небольшого двоичного файла. Взгляните на строку под Welcome to the Foo application. Похоже на неудачную попытку скрыть пароль или ключ!
Рис. 12.1 . «Сокрытие» пароля в приложении, написанном на языке С или С++
Для приложений, написанных на .NET–совместимом языке, например VB.NET, ]# или С#, можете воспользоваться программой ildasm.exe, поставляемой в составе .NET Framework SDK. Она позволяет выполнить тщательный анализ кода. Если вызвать ее, как показано ниже, то она выведет все строки. Посмотрите, нет ли среди встроенных паролей.