Шрифт:
Интервал:
Закладка:
SecurityManager security = System.getSecurityManager();
if (security != null)
{
security.checkXXX(argument, . . . );
}При разрешении операции функция check просто возвращает управление, при запрещении – возбуждает исключение SecurityException. Реализация по умолчанию для любого метода check* предполагает, что вызываемый метод не имеет права на выполнение данной операции. В обязанности менеджера безопасности, работающего с апплетами, входит защита от загрузки новых загрузчиков классов, защита потоков и групп потоков друг от друга, контроль за обращением к системным ресурсам, к ресурсам виртуальной машины, к сетевым соединениям и т. п.
Текущий менеджер безопасности устанавливается с помощью функции System.setSecurityManager, причем, если менеджер безопасности уже был установлен, эта функция также вызывает SecurityException.
В JDK 1.1 система безопасности получила дальнейшее развитие (рис. 10.2). Принципиально ничего не изменилось, но была добавлена возможность цифровой подписи классов – аналог сертификатов в ActiveX. Теперь можно решить, заслуживает ли подписанный удаленный код полного доверия, и не накладывать на него стандартные ограничения.
Рис. 10.2. Модель безопасности JDK 1.1Побочным эффектом введения этого механизма стало появление в составе стандартной библиотеки Java криптографических функций – Crypto API.
Модель безопасности JDK 1.1 отличалась черно-белым взглядом на мир – мы либо полностью доверяем загруженному коду, либо нет. В Java 2 (JDK 1.2), вышедшей в декабре 1998, была представлена новая гибкая модель безопасности, основанная на привилегиях и правах доступа (рис. 10.3).
Рис. 10.3. Модель безопасности JDK 1.2Дополнительно к существующим классам в Java 2 добавились:
• новый загрузчик классов java.security.SecureClassLoader;
• класс java.security.Permission, наследники которого используются для определения прав доступа к различным ресурсам, и класс java.security.PermissionCollection, позволяющий группировать права доступа. За доступ к файлам отвечает java.io.FilePermission, к сети – java.net.SocketPermission, к графическим ресурсам – java.awt.AWTPermission и т. д.;
• класс java.security.AccessController, используемый для контроля доступа FilePermission p = new FilePermission("/tmp/junk", "read"), и AccessController.checkPermission(p);
• класс java.security.ProtectionDomain, позволяющий объединить классы, которым предоставляются одинаковые права доступа;
• класс java.security.Policy, отвечающий за политику безопасности. В каждый момент активен только один объект Policy, считывающий настройки из файла конфигурации. В этом файле можно описать, какие права доступа связаны с той или иной подписью и/или местом расположения файлов:grant codeBase «http://www.somehost.com/*», signedBy «Signer»
{
permission java.io.FilePermission "/tmp/*", "read";
permission java.io.SocketPermission "*", "connect";
};Новая модель безопасности имеет следующие особенности:
• настраиваемый контроль доступа, дающий возможность указать, что код, обладающий определенными правами доступа, имеет право на определенные действия. Этого можно было добиться и раньше, с помощью создания специализированных менеджеров безопасности и загрузчиков классов, новая же архитектура значительно упростила такую процедуру;
• легко конфигурируемая политика безопасности. Опять же ключевое слово здесь – «легко»;
• возможность легкого расширения множества прав доступа, то есть фактически контролируемых действий. Если раньше это требовало введения новых методов check* в SecurityManager, теперь достаточно описать еще одного наследника класса java.security.Permission;
• усиление контроля за всеми Java-приложениями: в настоящее время никакой код не считается безопасным априори. Локальный код может быть подвержен тем же проверкам, что и код апплета, хотя, конечно, никто не мешает ослабить этот контроль с помощью настроек.
Таким образом, схема работы довольно гибкая и позволяет эффективно реализовать любую политику безопасности.Суровая действительность
До сих пор мы говорили о том, каким образом системы безопасности клиентских решений выглядят в идеальном случае, как это должно работать. Реально же большая часть проблем безопасности пользователей связана именно с ошибками в исполнении этих стройных схем.
Апплеты, мешающие работе
Java-приложения зависят от существующей для данной платформы виртуальной Java-машины. Java-машины реализованы для всех наиболее распространенных платформ, а также входят в состав самых популярных браузеров, но они достаточно ресурсоемки и зачастую довольно нестабильны. Кроме того, остаются проблемы совместимости – поскольку Java изначально проектировалась для написания многоплатформенных приложений, в нее преимущественно входили элементы, существующие на всех платформах, что привело к некоторой аскетичности доступных средств. Отдельные разработчики расширяют возможности виртуальных машин для конкретной платформы, и получается, что Java-приложение, использующее все эти возможности, утрачивает способность запускаться на других платформах.
Для начала рассмотрим ряд атак, прекрасно функционирующих в рамках существующей модели безопасности (на сегодняшний день – середина 1999 года – для большинства пользователей популярных браузеров таковой является схема из JDK1.1). Java достаточно хорошо справляется с защитой от нарушения целостности системы, но вот c оставшимися видами атак у нее явные проблемы. Большинство представленных здесь примеров прекрасно функционирует в Netscape Communicator 4.5. Internet Explorer 4.01 справляется с некоторыми из них намного лучше, но и у него есть «любимые мозоли». Так, один из вредоносных апплетов, приводящий к зависанию Windows 9x, активно использовал расширения Java от Microsoft, позволяющие работать напрямую с библиотеками DirectX.
Проще всего создаются апплеты, затрудняющие работу пользователя. К примеру, мы открываем какую-то WWW-страницу и вздрагиваем от несущегося из колонок раздражающего звука. Нами овладевает естественное желание немедленно уйти с этой страницы, но тут-то и выясняется, что звук никуда не делся и будет продолжаться до тех пор, пока мы не выйдем из браузера. А на дворе 2 часа ночи, в соседнем окне скачивается третий мегабайт 10-мегабайтного архива, сервер не поддерживает докачки и т. п. И вся проблема – в том, что автор апплета случайно или по злому умыслу пропустил код, выключающий звук при остановке апплета.
Мощное средство борьбы с пользователем – потоки. Они вовсе не обязаны остановиться при уходе со страницы, с которой был загружен апплет. В сочетании с установкой приоритета MAX_PRIORITY и обработчика исключительной ситуации ThreadDeath можно получить весьма живучего вредителя, который, к примеру, начнет следить за всеми запускаемыми апплетами и останавливать их потоки.
Еще один вариант сценария отказа в обслуживании (Denial of Service – DoS): открываем поток с большим приоритетом и начинаем искать в нем простые числа в диапазоне от 1 до 10100, не забывая насвистывать любимую мелодию, либо запускаем бесконечный цикл и создаем в нем окна размером, например, миллион на миллион пикселей (клавиатура и мышь у клиента будут заблокированы очень скоро):
while(true)
{
try
{
littleWindow = new bigFrame("Hello!");
littleWindow.resize(1000000, 1000000);
littleWindow.move(-1000, -1000);
littleWindow.show();
}
catch (OutOfMemoryError o)
{
repaint();
}
}
class bigFrame extends Frame
{
Label 1;
bigFrame(String title)
{
super(title);
setLayout(new GridLayout(1, 1));
Canvas whiteCanvas = new Canvas();
whiteCanvas.setBackground(Color.white);
add(whiteCanvas);
}
}Впрочем, DoS-атаки не слишком интересны, пишутся без особых усилий и порой без осознанного участия автора апплета. Чтобы убедиться в этом, достаточно походить по иным перегруженным апплетами страницам. У окон сверхбольшого размера есть еще один интересный аспект – в этом случае на экран просто не помещается стандартное сообщение о том, что пользователь видит окно, созданное апплетом. А здесь уже открываются широкие возможности для социальной инженерии (см. главу 2). Наконец, мы можем воспользоваться потоками, чтобы заставить посетителя немного поработать на нас. Конечно, апплет имеет право установить сетевое соединение только с хостом, с которого он был запущен, но нам большего и не надо. Пишем сервер, способный обмениваться информацией с апплетом, запускаем его на том же хосте – и все, распределенная система поиска очередного простого числа, поиска опровержения большой теоремы Ферма или просто подбора паролей готова к работе. Можно даже не изобретать какой-то свой протокол, а воспользоваться готовыми – получать очередное задание по http, отправлять результаты по SMTP, заодно узнать побольше о пользователе. Возможности Java в этом плане ограничены, но в нашем распоряжении есть JavaScript, на котором можно написать, к примеру, код, собирающий информацию об установленных у клиента дополнительных модулях и передающий ее апплету:
/* Unplugged.java by Mark D. LaDue */
/* April 15, 1998 */
/* Copyright (c) 1998 Mark D. LaDue */
import netscape.applet.*;
import netscape.javascript.*;
public class Unplugged extends java.applet.Applet implements Runnable{
Thread controller = null;
JSObject jso = null;
int numplugs = 0;
String npname = null;
String[] plugs = null;
int histlen = 0;
public void init() {