Добавлен: 29.10.2018
Просмотров: 48217
Скачиваний: 190
766
Глава 9. Безопасность
байт. Тогда она выводит количество ненулевых байтов с начала файла и осуществляет
выход. На рис. 9.33, б показан граф системного вызова, осуществляемого этой про-
граммой (где printf вызывает write).
Рис. 9.32. а — программа; б — граф системных вызовов для нее
Теперь предположим, что кто-то обнаруживает ошибку в этой программе, ухитряется
спровоцировать переполнение буфера и вставляет исполняемый вредоносный код.
Когда этот код запускается, он, вероятнее всего, выполняет другую последователь-
ность системных вызовов. Например, он может пытаться открыть какой-нибудь файл,
который ему нужно скопировать, или открыть сетевое подключение к домашнему
телефону. При первом же системном вызове, не вписывающемся в схему, тюремщик
точно знает, что предпринята атака, и может принять меры, например уничтожить
процесс и оповестить системного администратора. Этим способом система обнару-
жения проникновения может обнаружить предпринимаемые атаки. Статический
анализ системных вызовов — это всего лишь один из многих способов, используемых
в работе IDS.
При использовании такого обнаружения проникновения на основе модели тюремщик
должен знать модель (то есть граф системных вызовов). Наиболее целенаправленный
способ изучить модель состоит в том, чтобы заставить компилятор ее сгенерировать
и заставить автора программы подписать ее и приложить ее сертификат. Таким обра-
зом, любая попытка модифицировать исполняемую программу заранее будет обнару-
жена при запуске, поскольку реальное поведение не будет согласовываться с имеющим
цифровую подпись ожидаемым поведением.
К сожалению, хитрый атакующий злоумышленник может предпринять так называемую
мимикрическую атаку
(mimicry attack), в которой вставляемый код осуществляет те же
системные вызовы, которые ожидаются от программы, поэтому нужны более слож-
ные модели, чем те, которые только отслеживают системные вызовы. И тем не менее
системы IDS могут сыграть свою роль как часть глубоко эшелонированной обороны.
9.10. Средства защиты
767
Система IDS, основанная на модели, никоим образом не единственная в своем роде.
Многие IDS используют концепцию под названием приманка (honeypot), представ-
ляющую собой набор ловушек, которые привлекают и отлавливают взломщиков
и вредоносное программное обеспечение. Обычно это изолированная слабо защищен-
ная машина с внешне интересным и ценным содержимым, подготовленная к захвату.
Устроители приманки тщательно следят за любыми попытками любых атак в ее адрес,
чтобы изучить природу атаки. Некоторые системы IDS помещают свои приманки на
виртуальные машины, чтобы воспрепятствовать повреждению реальной базовой си-
стемы. Поэтому вполне естественно, что вредоносная программа старается определить,
как было рассмотрено ранее, не запущена ли она на виртуальной машине.
9.10.6. Инкапсулированный мобильный код
Вирусы и черви являются программами, которые попадают в компьютер без ведо-
ма и против воли его владельца. Тем не менее иногда люди в той или иной степени
намеренно импортируют и запускают на своих машинах внешний код. Обычно это
происходит следующим образом. В далеком прошлом (что в мире Интернета озна-
чает несколько лет назад) большинство веб-страниц были простыми статичными
HTML-файлами с небольшим количеством связанных с ними изображений. А сегод-
ня все больше веб-страниц содержат небольшие программы, называемые апплетами
(applets). Когда загружается веб-страница, содержащая апплеты, они извлекаются
и выполняются. Например, апплет может содержать заполняемую форму и вдобавок
к ней интерактивную справку по ее заполнению. Когда форма заполнена, она должна
быть куда-то отправлена по Интернету для последующей обработки. Таким образом
можно улучшить работу с формами налоговых деклараций, заказов товаров и многими
другими разновидностями форм.
Другим примером поставки программ от одной машины к другой для их выполнения
на машине назначения являются агенты (agents). Это программы, запускаемые поль-
зователем для выполнения некоторой задачи и возвращения отчета. Например, агенту
может быть поставлена задача проверки некоторых веб-сайтов бюро путешествий для
поиска самого дешевого перелета из Амстердама в Сан-Франциско. После прибытия
на каждый сайт агент будет запущен, получит необходимую ему информацию, а затем
переместится на следующий веб-сайт. Завершив работу, он может вернуться домой
и отчитаться о том, что ему удалось узнать.
Третьим примером мобильного кода может послужить файл PostScript, предназна-
ченный для распечатки на PostScript-принтере. Файл PostScript — это программа на
языке программирования PostScript, которая выполняется внутри принтера. Обычно
она приказывает принтеру нарисовать определенные кривые фигуры, а затем их за-
красить, но она может сделать и что-нибудь другое по своему усмотрению. Апплеты,
агенты и файлы PostScript — это только три примера среди множества других примеров
мобильного кода
(mobile code).
После состоявшегося ранее большого разговора о вирусах и червях должно быть ясно,
что, разрешив стороннему коду работать на вашей машине, вы подвергаете ее огромно-
му риску. Тем не менее кое-кому действительно нужно запускать сторонние программы,
поэтому возникает вопрос: может ли мобильный код быть безопасным? Если ответить
коротко, то да, но это не так просто дается. Основная проблема в том, что когда про-
цесс импортирует апплет или другой мобильный код в свое адресное пространство
768
Глава 9. Безопасность
и запускает этот код, он работает как часть вполне допустимого пользовательского
процесса и обладает всеми пользовательскими полномочиями, включая возможность
читать, записывать, удалять или шифровать имеющиеся на диске файлы пользователя,
отправлять электронную почту в дальние страны и делать многое другое.
Много лет назад в операционных системах разрабатывалась концепция процессов,
предусматривающая создание барьеров между пользователями. Замысел состоял
в том, что каждый процесс имеет собственное адресное пространство и собственный
идентификатор пользователя (UID), разрешающий пользоваться файлами и другими
ресурсами, которые принадлежат именно этому, но не другим пользователям. Для обе-
спечения защиты всех остальных частей процесса от одной его части (апплета) такая
концепция процесса не годилась. Потоки позволяют иметь внутри процесса несколько
потоков управления, но не дают ничего для защиты одного потока от другого.
Теоретически запуск каждого апплета в качестве отдельного процесса оказывает неко-
торую помощь, но реализация этой идеи довольно часто не представляется возможной.
Например, веб-страница может содержать два или более апплета, которые взаимодей-
ствуют друг с другом и с данными на веб-странице. Веб-браузер также может нуждать-
ся во взаимодействии с апплетами, осуществляя их запуск и остановку, снабжая их
данными и т. д. Если для каждого апплета запустить его собственный процесс, то все
это в целом не станет работать. Более того, запуск апплета в его собственном адресном
пространстве не усложняет ему задачу по хищению или повреждению данных, скорее
наоборот, — упрощает эту задачу, поскольку за ним там никто не следит.
Было предложено и реализовано множество новых методов работы с апплетами
(и в целом с мобильным кодом). Далее мы рассмотрим два таких метода: песочницу
и интерпретацию. Кроме этого для проверки надежности источника апплета может
также использоваться цифровая подпись кода. У каждого из этих методов есть свои
сильные и слабые стороны.
Песочницы
Первый метод, названный игрой в песочницу (sandboxing), является осуществляемой
во время выполнения попыткой загнать каждый апплет в ограниченный диапазон
виртуальных адресов (Wahbe et al., 1993). При этом виртуальное адресное про-
странство делится на одинаковые по размеру области, которые мы будем называть
песочницами. Каждой песочнице присуще использование в ее адресах одной и той же
строки старших разрядов. 32-разрядное адресное пространство может быть поделено
не более чем на 256 песочниц с 16-мегабайтными границами, чтобы все адреса внутри
песочницы имели одни и те же старшие восемь разрядов. Точно так же у нас могут
быть 512 песочниц с 8-мегабайтными границами, и у каждой песочницы будет 9-раз-
рядный адресный префикс. Размер песочницы должен выбираться с учетом того,
чтобы в ней смог поместиться самый большой апплет и чтобы при этом не терялось
впустую слишком много виртуального адресного пространства. Физическая память
не становится препятствием, если, как это часто и бывает, используется подкачка
страниц по запросу. Как показано на рис. 9.33, а, каждому апплету выделяются две
песочницы: одна для кода, а другая для данных, в рассматриваемом случае это 16
песочниц по 16 Мбайт каждая.
В основу песочниц положена необходимость гарантировать невозможность для ап-
плета передать управление коду за пределы его песочницы или сослаться на данные за
9.10. Средства защиты
769
Рис. 9.33. а — память, поделенная на песочницы по 16 Мбайт;
б — один из способов проверки команды на допустимость
пределами его песочницы данных. Смысл использования двух песочниц заключается
в том, чтобы помешать апплету изменить свой код во время выполнения и обойти эти
ограничения. Препятствуя любому сохранению данных в песочнице кода, мы устра-
няем опасность, исходящую от самомодифицирующегося кода. Пока апплет имеет
такие ограничения, он не может нанести повреждения браузеру или другим апплетам,
посадить вирус в память или повредить память каким-нибудь другим способом.
После того как апплет будет загружен, он перемещается в начало своей песочницы.
Затем осуществляется проверка, не выходят ли ссылки на код и данные за пределы со-
ответствующих песочниц. В дальнейшем будут рассмотрены только ссылки на код (то
есть команды JMP и CALL), но то же самое делается и в отношении ссылок на данные.
Статическую команду JMP, использующую непосредственную адресацию, проверить до-
вольно легко. Нужно лишь ответить на вопрос: находится ли адрес назначения за преде-
лами песочницы кода? Точно так же нетрудно проверить и команды JMP, использующие
относительную адресацию. Если апплет содержит код, пытающийся передать управление
за пределы песочницы, он отклоняется и не выполняется. Точно так же попытки доступа
к данным за пределами песочницы данных становятся причиной отбраковки апплета.
Труднее разобраться с динамическими командами JMP. У большинства машин имеются
команды, в которых адрес перехода вычисляется во время выполнения, помещается
в регистр, а затем производится опосредованная передача управления, например коман-
да JMP(R1) предписывает передачу управления по адресу, содержащемуся в регистре 1.
Допустимость такой команды может быть проверена в ходе выполнения. Это делается
путем вставки кода непосредственно перед опосредованной передачей управления,
чтобы проверить адрес назначения. Пример такой проверки показан на рис. 9.33, б.
Вспомним, что все допустимые адреса имеют одни и те же старшие k разрядов, поэто-
му этот префикс может быть помещен в рабочий регистр, скажем в S2. Этот регистр
не может быть использован самим апплетом, что может потребовать его перезаписи,
чтобы избежать использования этого регистра.
770
Глава 9. Безопасность
Этот код работает следующим образом. Сначала проверяемый адрес назначения ко-
пируется в рабочий регистр S1. Затем этот регистр сдвигается вправо в точности на то
количество разрядов, которое необходимо, чтобы изолировать общий префикс в S1.
Затем изолированный префикс сравнивается с правильным префиксом, загруженным
в S2. Если они не совпадают, происходит системное прерывание и апплет уничтожа-
ется. Эта последовательность кода требует четырех команд и двух рабочих регистров.
Внесение изменений в двоичную программу в ходе ее выполнения требует опреде-
ленных, но не запредельных усилий. Эта работа может упроститься, если апплет был
представлен в исходном виде, а затем скомпилирован локально с использованием
надежного компилятора, автоматически проверяющего статические адреса и встав-
ляющего код для проверки в ходе выполнения динамических адресов. В любом слу-
чае в результате динамических проверок в ходе выполнения программы возникают
определенные издержки. Вахбе (Wahbe et al., 1993) оценил их примерно в 4 %, что,
в общем-то, вполне приемлемо.
Вторая проблема, требующая решения, связана с попыткой апплета осуществить систем-
ный вызов. Решение в данном случае очевидно. Команда системного вызова заменяется
вызовом специального модуля, который называется монитором обращений (reference
monitor), таким же образом, как проводилась вставка проверки динамических адресов
(или, если доступен исходный код, путем компоновки со специальной библиотекой,
вызывающей монитор обращений вместо осуществления системных вызовов). В любом
случае монитор обращений проверяет каждую попытку вызова и решает, насколько она
безопасна при выполнении. Если вызов считается допустимым, с его помощью, напри-
мер, записываются данные во временный файл в определенном рабочем каталоге, его
разрешается обработать. Если выясняется, что вызов опасен, или монитор обращений
ничего не может сообщить, апплет уничтожается. Если монитор обращений может со-
общить о том, какой апплет его вызвал, то где-нибудь в памяти может находиться единый
монитор обращений, способный обрабатывать запросы от всех апплетов. Обычно свои
полномочия монитор обращений устанавливает из конфигурационного файла.
Интерпретация
Второй способ выполнения ненадежных апплетов заключается в их запуске в режиме
интерпретации и запрещении им получения контроля над оборудованием. Такой под-
ход используют веб-браузеры. Апплеты веб-страниц чаще всего пишутся на обычном
языке программирования Java или высокоуровневом языке сценариев, таком как
safe-TCL или JavaScript. Java-апплеты сначала компилируются в виртуальный стек-
ориентированный машинный язык, называемый JVM (Java Virtual Machine — вир-
туальная машина Java). Затем эти JVM-апплеты помещаются на веб-страницу. При
загрузке они вставляются в JVM-интерпретатор внутри браузера (рис. 9.34).
Преимущества запуска интерпретируемого кода над запуском скомпилированного
кода в том, что каждая команда перед выполнением проверяется интерпретатором.
Это дает интерпретатору возможность проверить допустимость адресов. Кроме этого,
могут быть перехвачены и интерпретированы системные вызовы. Порядок обработки
этих вызовов является вопросом политики безопасности. Например, если апплет счи-
тается надежным (например, он поступил с локального диска), его системные вызовы
могут выполняться без вопросов. Но если апплет считается ненадежным (например,
он поступил из Интернета), он может быть помещен в своеобразную песочницу, огра-
ничивающую его поведение.