\chapter{Безопасность}

\begin{epygraph}
Just cause you're paranoid don't mean they aren't after you.

\flushright Nirvana.

То, что я параноик, еще не означает, что никто не намерен
причинить мне вред.

\flushright Нирвана.
\end{epygraph}

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

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

\note{Часто взлом систем и написание вирусов ради развлечения
приравнивают к обычному вандализму. С точки зрения авторов, эти действия
все-таки отличаются от битья стекол или отрывания трубок у
телефонов-автоматов: вандализм представляет собой акт чистого разрушения,
в то время как взлом компьютерной системы требует некоторой
интеллектуальной деятельности, которую при желании можно расценить
как созидательную. Впрочем, с точки зрения пострадавших разницы между
этими действиями практически нет: эмоции пользователя, у которого пропал
день работы в результате вирусной активности, мало отличаются от эмоций
человека, который не может вовремя позвонить по телефону, и сильнее всего
в обоих случаях угнетает явная бессмысленность вредоносного действия.}

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

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

В популярных публикациях, посвященных теме компьютерной безопасности,
часто приходится видеть аналогию с известным соревнованием снаряда и брони:
после появления более мощной брони всегда появляются способные ее преодолеть
снаряды и так далее до бесконечности. На взгляд авторов, такая аналогия весьма
неудачна.

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

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

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

Если же строить защиту на принципе <<разрешено все, кроме того, что 
запрещено>> , то аналогия с броней и снарядом оказывается вполне актуальна: 
разработчик системы безопасности перекрывает лишь пути, до которых дошла его 
фантазия, все же остальное пространство методов доступа оказывается 
неперекрыто. Характерным примером может служить эволюция антивирусных 
средств для \sysname{DOS} и параллельная эволюция технологий разработки 
вирусов.

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

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

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

{\small
Широко известна известна ошибка в программое \verb"fingerd", входившей
в систему \sysname{BSD Unix}.
Эта программа принимала данные в буфер, выделяемый в локальном
стеке, не проверяя, входит ли принимаемая порция данных в буфер.
Искусственно вызванное переполнение могло привести к перезаписи стекового
кадра процедуры, которое в свою очередь при возврате из подпрограммы
могло привести к передаче управления в нежелательное место.

\term{<<Червяк Морриса>>} (\cite{Worm}) пользовался этой дырой, передавая
кусок кода и поддельный стековый кадр, передававший управление на этот код.
Код в свою очередь запускал на целевой системе копию командного
интерпретатора, исполнявшуюся с привилегиями суперпользователя,
Затем червь использовал полученный командный интерпретатор для втягивания
в систему своего тела.

Ошибка была обнаружена в 1987 г. и вскоре после обнаружения была
исправлена. Практически все современные системы используют безопасную
в этом отношении версию \verb"fingerd".

Аналогичная ошибка была обнаружена в 1995 году в программе \verb"syslogd"
некоторых коммерческих версий \sysname{Unix}.

В обоих случаях ошибка
связана с использованием библиотечной функции \verb"gets(char * buf)",
которая принимает ограниченную символом \verb"'\n'" строку данных из
стандартного потока ввода, не проверяя буфер на переполнение.
В настоящее время функция \verb"gets" поддерживается лишь для
совместимости и использовать ее не рекомендуется. Вместо нее стандарты
\standname{ANSI C} и \standname{POSIX} рекомендуют использовать
функцию \verb"fgets(FILE * stream, size_t bufsize, char * buf);"},
которая осуществляет проверку на переполнение.

В январе 1996 года была обнаружена проблема, создаваемая
одним из антивирусных пакетов для \sysname{Windows NT}.
Этот пакет предназначен не для ловли вирусов в самой \sysname{NT},
потому что аналоги вирусов ДОС в защищенных многопользовательских
ОС практически нежизнеспособны. Он предназначен для использования на
системах, работающих в качестве файлового сервера и хранящих на
своих дисках прикладные программы для \sysname{DOS/MS Windows},
используемые станциями локальной сети.

Рабочая часть пакета состоит
из процесса-демона (или, по терминологии \sysname{NT},
\termin{сервиса}{service}), который периодически сканирует исполняемые
файлы на дисках с целью поиска в них сигнатур известных вирусов.
Сервисный процесс исполняется с привилегиями администратора для того,
чтобы установленные пользователями атрибуты защиты от чтения или
записи не мешали сканированию или <<лечению>> зараженных файлов.

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

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

Обычно выдача санкций (далее мы будем обычно использовать словосочетание
\term{права доступа}) осуществляется на основе пользовательских
идентификаторов. Каждому пользователю системы выделяется идентификатор.
Обычно такой идентификатор имеет две формы: числовой код, используемый
внутри системы, и мнемоническое символьное имя, используемое при общении
с пользователем.

{\small
Например, в системах семейства \sysname{Unix} пользователь индентифицируется
целочисленным значением \termin{uid}{user identifier}. Пользователь может
иметь также символьное имя. Соответствие между символьным и числовым
идентификаторами устанавливается на основе содержимого текстового файла
\verb"/etc/passwd". Каждая строка этого файла описывает одного пользователя
и состоит из семнадцати полей, разделенных символом \verb"':'". В первом поле
содержится символьное именя пользователя, во втором -- числовой идентификатор
в десятичной записи. Остальные поля содержат другие сведения о пользователе,
например, его полное имя.

Пользовательские программы могут устанавливать соответствие между числовым
и символьным идентификаторами самостоятельно, путем просмотра файла
\verb"/etc/passwd", или использовать библиотечные функции, определенные
стандартом \standname{POSIX}. Во многих реализациях эти функции используют
вместо \verb"/etc/passwd" индексированную базу данных, а сам файл
\verb"/etc/passwd" сохраняется лишь для совместимости со старыми
программами.

Нужно отметить, что соответствие между символьным и числовым идентификаторами
в \sysname{Unix} не является взаимно однозначным. Одному и тому же
числовому идентификатору может соответствовать несколько имен. Кроме того,
в \sysname{Unix} можно создать объекты с числовым uid, которому не
соответствует никакого символьного имени.
}

Чаще всего используется принцип \term{сессий} работы с машиной. В начале
работы пользователь устанавливает соединение и <<входит>> в систему. При
<<входе>> происходит его идентификация. Затем пользователь проводит сеанс
работы с системой, а по завершении этого сеанса разрегистрируется. Сеанс
или сессия жестко связаны с определенным терминалом и/или определенной
машиной в сети. При работе в сети есть определенные нюансы,
но они будут обсуждаться ниже.

Большинство современных ОС позволяют также запускать задания без входа
в систему и создания сессии. Так, системы семейства \sysname{Unix}
предоставляют возможность пользователям запускать задачи в заданные
моменты астрономического времени или периодически, например, в час ночи
в пятницу каждой недели. Каждая такая задача исполняется от имени
определенного пользователя -- того, кто запросил запуск задачи.
Для управления правами доступа в таких ситуациях идентификатор пользователя
ассоциируется не с сессией, а с отдельными заданиями, а обычно даже с
отдельными задачами.

Так, в системах семейства \sysname{Unix} с каждой задачей (процессом)
связано два идентификатора пользователя: \term{реальный} и
\term{эффективный}. В большинстве ситуаций эти идентификаторы совпадают.
Таким образом, каждая задача обязательно исполняется от имени определенного
пользователя.

Большинство современных ОС общего назначения также связывают с каждой
задачей идентификатор пользователя или \term{контекст доступа}{security
context}. Исключением в этом отношении является \sysname{OS/2}, в которой
такого связывания не происходит. \sysname{OS/2} позволяет ассоциировать
пользовательские идентификаторы и права доступа с файлами, но не с задачами.
Идентификаторы хозяина файла используются программами сетевого разделения
файлов, например \progname{IBM LAN Server}, но вся проверка прав доступа и
идентификация пользователя происходит внутри задачи-сервера, а не на уровне ОС.

Отсутствие раздельных контекстов доступа у задач не позволяет использовать
\sysname{OS/2} как многопользовательскую систему. Это дает основание
многим критикам трактовать название системы как <<ОС пополам>>: система,
в которой отсутствует половина функций, требуемых от современной
ОС общего назначения.

\section{Идентификация пользователя}
\begin{epygraph}
\begin{verse}
Петли дверные\\
Многим скрипят, многим поют:\\
<<Кто вы такие,\\
Вас здесь не ждут!>>\\
\end{verse}
\flushright В.\,Высоцкий.
\end{epygraph}

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

По-английски процесс входа в систему называется \termin{login}{log in}
и происходит от слова \term{log}, которое обозначает регистрационный
журнал или процесс записи в такой журнал. Наиболее точным переводом
слова \term{login}\footnote{В обычном английском языке такого слова
нет, но в компьютерной лексике слова \term{login} и \term{logout}
прижились очень прочно.} является \term{регистрация}. Соответственно,
процесс выхода называется \term{logout}. Его точная русскоязычная калька
-- \term{разрегистрация} -- к сожалению, очень неблагозучна

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

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

Использование паролей основано на следующих трех предположениях:
\begin{itemize}
\item пользователь может запомнить пароль;
\item никто не сможет догадаться, какой
пароль был выбран;
\item пользователь никому не расскажет свой пароль.
\end{itemize}

Последнее предположение кажется разумным: если человек может добровольно
кому-то рассказать свой пароль, с примерно той же вероятностью этот
человек может сам сделать пакость. Впрочем, человека можно {\it заставить}
рассказать пароль\ellipsis Однако защита от таких ситуаций требует особых
мер, которые не могут быть обеспечены на уровне операционной системы.

Если вдуматься, первые два требования
отчасти противоречат друг другу. Количество легко запоминаемых паролей
ограничено, и перебрать все такие пароли оказывается не так уж сложно.
Известный в истории сети \entname{Internet} <<червяк Морриса>> %
\footnote{<<Червяк Морриса>> заслуживает отдельного и весьма подробного
обсуждения. Эта программа в 1987 г. поразила несколько сотен узлов
сети \entname{Inernet}. Червь производил комплексную и довольно хорошо
продуманную атаку, продемонстрировавшую не только специфические изъяны
безопасности тогдашних ОС семейства \sysname{Unix}, но и несколько
фундаментальных проблем компьютерной безопасности. По мнению
авторов, серьезное изучение проблем компьютерной безопасности в наше время
невозможно без учета опыта <<червя Морриса>>.} \cite{Worm}.
использовал при подборе паролей следующие варианты:
\begin{itemize}
\item входное имя пользователя;
\item входное имя с символами в обратном порядке -- <<задом наперед>>;
\item компоненты полного имени пользователя -- имя (first name),
фамилию (last name) и другие компоненты, если они есть;
\item те же компоненты, но задом наперед;
\item слова из заранее определенной таблицы из 400 элементов;
Большая часть успешно подобранных паролей была взята из этой таблицы.
После поимки червя таблица была опубликована с тем, чтобы пользователи
не повторяли старых ошибок\footnote{Эта таблица приводится в \cite{Worm}.
По мнению авторов, включение ее в данное издание нецелессобразно,
поскольку она рассчитана на англоязычных пользователей и не очень
актуальна в России.}.
\end{itemize}

С первого взгляда видно, что таких <<легко запоминаемых>> вариантов
довольно много -- никак не меньше нескольких сотен. Набор их
вручную занял бы очень много времени, но для компьютера несколько сотен
вариантов -- это доли секунды. Хотя\ellipsis Первый слой защиты от
подбора пароля заключается именно в том, чтобы увеличить время подбора.
Обнаружив неправильный пароль, наученные горьким опытом современные системы
делают паузу, прежде чем позволят повторную попытку входа с той же линии.
Такая пауза может длиться всего одну секунду, но даже этого достаточно,
чтобы превратить процесс подбора пароля из долей секунды в десятки минут.

Второй слой защиты заключается в том, чтобы усложнить пароль и тем самым
увеличить количество вариантов. Даже очень простые усложнения сильно
увеличивают перебор. Так, простое требование использовать в пароле буквы
и верхнего и нижнего регистров увеличивает перебор в $2^{n}$ раз, где
$n$ -- это длина пароля. В большинстве современных систем пароль обязан
иметь длину хотя бы шесть символов, т.е. количество вариантов
увеличивается в 64 раза. Требование использовать в пароле хотя бы один
символ, не являющийся буквой, увеличивает число вариантов в
$6 \times 43 = 258$ раз (в наборе \standname{ASCII} 43 небуквенных
графических символа). Вместо десятков минут подбор пароля, который обязан
содержать буквы разных регистров и хотя бы один спецсимвол, займет много
дней.

Но если взломщику действительно {\it нужно} попасть в систему, он может
подождать и несколько дней, поэтому необходим третий слой защиты --
ограничение числа попыток. Все современные системы позволяют задать
число неудачно набранных паролей, после которого имя блокируется.
Это число всегда больше единицы, потому что пользователь -- это человек,
а людям свойственно ошибаться, но в большинстве случаев такой предел
задается не очень большим -- обычно 5 - 7 попыток. Однако, этот метод имеет
и оборотную сторону -- его можно использовать для блокировки пользователей.

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

Наконец, последний слой защиты -- это оповещение пользователя (а иногда
и администратора системы) о неудачных попытках входа. Если пользователь
сам только что нажал не ту кнопку, он при входе увидит, что была одна
неудачная попытка, и не будет беспокоиться; однако, если есть сообщения
о дополнительных неудачных попытках, время побеспокоиться и разобраться,
что же происходит.

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

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

\begin{itemize}
\item Если бы администратор системы знал пользовательские пароли,
то взломщик, сумевший выдать себя за администратора, так же получал бы
доступ к этим паролям. После этого взломщик мог бы легко маскироваться
под других пользователей, что сильно усложнило бы обнаружение взлома.
\item Если администратор знает только административный пароль, для лишения
его административных привилегий достаточно сменить этот пароль.
Если же бывший администратор имел доступ ко всей базе данных о паролях,
он по прежнему будет иметь возможность доступа к системе, что может
оказаться нежелательным.
\end{itemize}

Для обеспечения секретности паролей обычно используют одностороннее
шифрование, при котором по зашифрованному значению нельзя восстановить
исходное слово. При этом программа аутентикации кодирует введенный пароль
и сравнивает полученное значение с хранящимся в базе данных.
При использовании достаточно сильных алгоритмов шифрования, например
\standname{DES}, узнать реальное значение пароля можно только путем полного
перебора всех возможных значений и сравнения зашифрованной строки со
значением в базе данных.

Отцам-основателям \sysname{Unix} этот механизм обеспечения секретности
показался настолько надежным, что они даже не стали ограничивать доступ
обычных пользователей к базе данных, и выделили для хранения зашифрованных
паролей поле в общедоступном для чтения файле \verb"/etc/passwd".

Однако <<червь Морриса>> продемонстрировал, что для подбора паролей
не нужно перебирать все возможные комбинации символов, поскольку
пользователи имеют тенденцию выбирать лишь ограниченное подмножество
таких комбинаций. Пользователь, имеющий возможность непосредственно
читать закодированные значения паролей, может осуществлять подбор,
не обращаясь к системным механизмам аутентикации, то есть делать
это очень быстро и практически незаметно для администратора.
Этот тип атаки называется \termin{словарной атакой}{dictionary attack}
и весьма опасен для систем, где пользователи неосторожны в выборе паролей.

После осознания опасности словарных атак разработчики систем семейства
\sysname{Unix} перенесли значения паролей в недоступный для чтения
файл \verb"/etc/shadow", где пароли также хранятся в односторонне
зашифрованном виде. Это значительно усложняет реализацию словарной
атаки, поскольку перед тем, как начать атаку, взломщик должен получить
доступ к системе с привилегиями администратора.

Практически все современные системы хранят данные о паролях в односторонне
зашифрованном виде в файле, недоступном обычным пользователям для чтения.
Поставщики некоторых систем, например \sysname{Windows NT}, даже
отказываются публиковать информацию о формате этой базы данных,
хотя это само по себе вряд ли способно помешать квалифицированному
взломщику.

\section{Идентификация пользователя в сети}

Когда пользователь работает с консоли компьютера или с терминала,
физически прикрепленного к терминальному порту (\term{hardwired}),
модель сессий является вполне приемлемой. При регистрации пользователя
создается сессия, ассоциированная с данным терминалом, и далее проблем
нет.

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

Напротив, в сетях с коммутацией пакетов, к которым относится большинство
современных сетевых протоколов (\protname{TCP/IP}, \protname{IPX/SPX},
\protname{ISO/OSI} и т.д.), нет физического понятия соединения.
В лучшем случае сетевой протокол предоставляет возможность
создавать виртуальные соединения с <<надежной>> связью, в которых
гарантируется отсутствие потерь пакетов и сохраняется порядок их
поступления. С таким виртуальным соединением вполне можно ассоциировать
сессию, как это сделано в протоколах \protname{telnet}, \protname{rlogin/rsh}
и \protname{ftp}.

{\small
Протокол \protname{telnet} используется для эмуляции алфавитно-цифрового
терминала через сеть. Пользователь устанавливает соединение и регистрируется
в удаленной системе таким же образом, как он регистрировался бы с физически
подключенного терминала. Например, в системах семейства \sysname{Unix}
создается виртуальное устройство, \termin{псевдотерминал}{pseudoterminal}
\verb"/dev/ptyXX", полностью эмулирующее работу физического терминала, и
система запускает ту же программу идентификации пользователя
\verb"/usr/bin/login", которая используется для физических терминалов.
При окончании сессии соединение разрывается, и псевдотерминальное устройство
освобождается.
}

Виртуальные сессии вынуждены полагаться на то, что сетевое программное
обеспечивает действительно гарантированное соединение, т.е., что никакая
<<левая>> машина не может вклиниться в соединение и прослушать его
или послать свои поддельные пакеты. Обеспечение безопасности на этом уровне
представляет собой отдельную проблему, обсуждение которой увело бы нас
далеко от основной темы.

В протоколах, использующих датаграммные соединения, средствами
протокола виртуальную сессию создать невозможно. Обычно в этом случае 
каждый пакет содержит идентификатор пользователя или сессии, от имени 
которого (в рамках которой) этот пакет послан.
Такой подход используется в протоколах работы с файловыми серверами
\protname{NFS}, и \protname{NCP} (Netware Core Protocol, используемый
файловыми серверами \sysname{Novell Netware}). Понятно, что чисто
датаграммные протоколы оказываются гораздо более уязвимы для поддельных
пакетов и прочих вредоносных воздействий. 

{\small
Например, \protname{NCP}, работающий через датаграммный протокол 
\protname{IPX}, реализует свой собственный механизм поддержки сессий:
все пакеты данной сессии имеют 8-битный номер и пакеты с неправильными
номерами просто игнорируются. Одна из программ взлома \sysname{Netware}
в течении короткого времени посылала 256 пакетов с различными номерами
-- хотя бы один пакет оказывался удачным. Для борьбы с такими действиями
в \sysname{Netware 3.12} была введена криптографическая подпись пакетов
в пределах сессии.
}

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

{\small
Интересное решение этой задачи предлагается при регистрации
на сервере \sysname{Novell Netware 3.x}: при входе пользователя на сервер
исполняется специальный командный файл, так называемый \term{login script}.
Если в нем содержится команда присоединения к другому серверу,
программа сначала пробует тот же пароль, который был задан при входе
на первый из серверов. Только если этот пароль не подходит,
система требует у пользователя ввода пароля.
Этот метод обеспечивает одновременную регистрацию на нескольких
серверах, но не подходит для динамического создания и разрыва соединений.
}

Обычно для автоматической регистрации используется модель
<<доверяемых>> систем (\term{trusted hosts}). Если система {\bf B}
\term{доверяет} системе {\bf A}, то все пользователи, зарегистрированные
на системе {\bf A}, автоматически получают доступ к системе {\bf B}
под теми же именами. Иногда аналогичную возможность можно предоставлять
для каждого пользователя отдельно -- пользователь сообщает, что при
регистрации входа с системы {\bf A} пароля запрашивать не надо.

{\small
Например, протоколы \protname{rlogin/rsh}, обеспечивающие запуск отдельных
команд или командного процессора на удаленной системе, используют
файл \verb"/etc/hosts.equiv" или \verb".rhosts" в домашнем каталоге
пользователя на удаленной системе. Файл \verb"/etc/hosts.equiv"
содержит имена всех машин, которым наша система полностью доверяет.
Файл \verb".rhosts" состоит из строк формата.

\begin{verbatim}
имя.удаленной.машины          имя_пользователя
\end{verbatim}

При этом \verb"имя.удаленной.машины" не может быть произвольным, оно
обязано содержаться в файле \verb"/etc/hosts", в котором собраны имена
и адреса всех удаленных машин, <<известных>> системе. То же требование
обязательно и для машин, перечисленных в \verb"/etc/hosts.equiv".

Например, пользователь \verb"fat" на машине \verb"iceman.cnit.nsu.ru"
набирает команду \verb"rlogin -l fat Indy.cnit.nsu.ru": войти в систему
\verb"Indy.cnit.nsu.ru" под именем \verb"fat". Если домашний каталог
пользователя \verb"fat" на целевой машине содержит файл \verb".rhosts",
в котором есть строка

\begin{verbatim}
iceman.cnit.nsu.ru           fat
\end{verbatim}

\noparskip то \verb"fat" получит доступ к системе \verb"Indy" без набора пароля.
Того же эффекта можно добиться для всех одноименных пользователей,
если \verb"/etc/hosts.equiv" содержит строку

\begin{verbatim}
iceman.cnit.nsu.ru
\end{verbatim}

Если же \verb"fat" наберет команду \verb"rlogin -l root Indy.cnit.nsu.ru",
а в домашнем каталоге пользователя \verb"root" файла \verb".rhosts"
нет или он не содержит вышеприведенной строки, команда \verb"rlogin"
потребует ввода пароля, независимо от содержимого файла
\verb"/etc/hosts.equiv". Нужно отметить, что администраторы обычно
вообще не разрешают использовать \verb"rlogin" для входа под именем
\verb"root", потому что этот пользователь является администратором системы
и обладает очень большими привилегиями.
}

Модель доверяемых систем обеспечивает большое удобство для пользователей
и администраторов и в различных формах предоставляется многими сетевыми
ОС. Например, в протоколе разделения файлов \protname{SMB} (в обиходе этот
протокол часто называют \protname{NetBIOS}, хотя это и не совсем
правильно), используемом
в \sysname{Windows for Workgroups}, \sysname{OS/2}, \sysname{Windows NT},
\sysname{Linux} и др., испольуется своеобразная модель аутентикации,
которую можно рассматривать как специфический случай доверяемых систем.

{\small
Аутентикация в \protname{SMB} основана на понятии \termin{домена}{domain}.
Каждый разделяемый ресурс (каталог, принтер и т.д.) принадлежит к
определенному домену, хотя и может быть защищен собственным паролем.
При доступе к каждому новому ресурсу необходимо подтвердить имя пользователя
и пароль, после чего создается сессия, связанная с этим ресурсом.
Для создания сессии используется надежное соединение, предоставляемое
сетевым протоколом нижнего уровня -- именованная труба \protname{NetBEUI}
или соединение \sysname{TCP}. Ввод пароля при каждом доступе неудобен
для пользователя, поэтому большинство клиентов -- 
\sysname{Windows for Workgroups}, \sysname{Windows 95}, \sysname{Windows NT}, 
\sysname{OS/2} --  просто запоминают пароль, введенный при регистрации в 
домене, и при подключении к ресурсу первым делом пробуют этот пароль.
Благодаря этому удается создать у пользователя иллюзию однократной 
регистрации. Кроме того, если сессия по каким-то причинам оказалась разорвана,
например из-за перезагрузки сервера, то можно реализовать прозрачное
для пользователя восстановление этой сессии.

С точки зрения клиента нет смысла говорить о межмашинном доверии -- клиенту
в среде \protname{SMB} никто не доверяет, и вполне справедливо: обычно
это система класса ДОС, не заслуживающая доверия. 

Однако серверы обычно передоверяют проверку пароля и идентификацию
пользователя выделенной машине, называемой \term{контроллером домена}{domain 
controller}. Домен обязан иметь один основной (primary) контроллер и может 
иметь несколько резервных (backup). При поступлении запроса на соединение
сервер получает у клиента имя пользователя и пароль, но вместо сверки
с собственной базой данных он пересылает их контроллеру домена и принимает
решение о принятии или отвергании сессии на основании вердикта, вынесенного
контроллером. 

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

С точки зрения безопасности доверяемые системы имеют
два серьезных недостатка:
\begin{enumerate}
\item Прорыв безопасности на одной из систем означает,
по существу, прорыв на всех системах, которые доверяют первой.
\item Возникает дополнительный тип атаки на систему безопасности:
машина, которая выдает себя за доверяемую, не являясь таковой.
\end{enumerate}

Первая проблема является практически неизбежной платой за разрешение
автоматической регистрации. Наиболее ярко эта проблема была
проиллюстрирована упомянутым выше <<Червем Морриса>> \cite{Worm},
который, проникнув в одну из систем, использовал содержимое файлов
\verb".rhosts" и \verb"/etc/hosts.equiv"
для проникновения в доверяющие системы, полагаясь на то, что межсистемное
доверие обычно делают взаимным.

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

Вторая проблема может быть отчасти решена использованием средств,
предоставляемых сетевыми протоколами, например, привязкой всех
логических имен доверяемых систем к их физическим сетевым адресам.
В протоколах \protname{TCP/IP} это может быть сделано с использованием
протокола \termint{\protname{arp}}{Address Resolution Protocol}{протокол
разрешения адресов}.

Более изощренный и, по-видимому, намного более надежный метод основан на
использовании алгоритма двухключевого шифрования \standname{RSA} или
родственных ему механизмов.

\subsection{Криптографические методы идентификации}

Алгоритм шифрования \standname{RSA} использует два парных ключа. Сообщение,
зашифрованное любым из ключей, может быть расшифровано другим.
При этом если нам доступен только один из ключей, мы не можем восстановить
другой. Длина ключей составляет несколько десятков или даже сотен байт,
поэтому подбор ключей на современной вычислительной технике оказывается
сильно затруднен.
Обычно более длинный ключ называют \termin{<<частным>>}{private},
а более короткий -- \termin{общедоступным}{public}, но для нас это различие
не очень существенно. Подробное описание алгоритма \standname{RSA}
можно найти в документе \cite{RSA}.

Способы аутентикации, основанные на \standname{RSA}, сводятся к
следующему механизму:
\begin{itemize}
\item Система {\bf A} генерирует последовательность байт, обычно случайную,
кодирует ее своим ключом и посылает системе {\bf B}.
\item Система {\bf B} раскодирует ее своим ключом. Это возможно, только
если системы владеют парными ключами.
\item Системы тем или иным способом обмениваются <<правильными>>
значениями зашифрованной посылки.
\end{itemize}

{\small
Для примера рассмотрим принцип \standname{RSA}-аутентикации в пакете
\verb"ssh" \cite{ssh} -- Secure Shell, разработанной Тату Илоненом
(Tatu Ylonen).
Пакет представляет собой функциональную замену программ \verb"rlogin/rsh"
и соответствующего этим программам демона \verb"rshd". В пакет входят
программы \verb"ssh" (клиент) и \verb"sshd" (сервер), а также утилиты для
генерации ключей \standname{RSA} и управления ими. \verb"ssh" использует
\standname{RSA} для прозрачной аутентикации пользователя при входе на
удаленную систему. Кроме того, \verb"ssh/sshd" могут осуществлять шифрование
данных, передаваемых по линии во время сеанса связи, и выполнять ряд других
полезных функций.

Сервер хранит список известных общедоступных ключей для каждого
из пользователей в файле \verb"$HOME/.ssh/authorized_keys", где
\verb"$HOME" обозначает домашний каталог пользователя.
Файл состоит из строк формата \verb"host_name:key" -- по строке для
каждого из разрешенных клиентов.
В свою очередь, каждый клиент хранит в файле \verb"$HOME/.ssh/private_key"
свой частный ключ.

Когда с удаленной системы-клиента приходит запрос на аутентикацию,
\verb"sshd" запрашивает общедоступный ключ. Если полученный ключ
совпадает с хранящимся в файле значением для этой системы, сервер
генерирует случайную последовательность из 256 бит, шифрует ее
публичным ключом и посылает клиенту. Клиент расшифровывает посылку
своим личным ключом, вычисляет 128-битовую контрольную сумму и
возвращают ее серверу. Сервер сравнивает полученную последовательность
с правильной контрольной суммой и принимает аутентикацию в случае
совпадения. Теоретически контрольные суммы могут совпасть и в случае
несовпадения ключей, но вероятность такого события крайне мала.
}

Методы, основанные на \standname{RSA} и других алгоритмах шифрования,
не могут решить проблемы распространения прорыва безопасности между
доверяемыми системами: проникший в доверяемую систему взломщик получает
доступ к <<частным>> ключам и может использовать их для немедленной
регистрации в любой из доверяющих систем или даже скопировать ключи
для проникновения в эти системы в более удобное время.
Однако, как уже говорилось выше, это является практически неизбежной
платой за разрешение автоматической регистрации в нескольких системах.

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

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

\section{Права доступа}

Существует несколько моделей предоставления прав доступа к файлам и
другим объектам. Наиболее простая модель используется в системах семейства
\sysname{Unix}.

В этих системах каждый файл или каталог имеют идентификаторы
хозяина и \term{группы}. Определено три набора прав доступа: для
хозяина, группы (т.е., для пользователей, входящих в
группу, к которой принадлежит файл) и всех остальных.
Пользователь может принадлежать к нескольким группам одновременно,
файл всегда принадлежит только одной группе.

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

Пр\'{а}ва на удаление или переименование файла не существует; вообще,
в \sysname{Unix} не определено операции удаления
файла как таковой, а существует лишь операция удаления имени
\verb"unlink"\footnote{Это связано с тем, что файл в \sysname{Unix}
может иметь несколько имен, и собственно удаление происходит только
при уничтожении последнего имени. Подробнее см. гл. \ref{FileSystems}.}.
Для удаления или изменения имени достаточно иметь право записи
в каталог, в котором это имя содержится.

В традиционных системах семейства \sysname{Unix} все глобальные объек\-ты
-- внешние устройства и именованные программные каналы -- являются
файлами (точнее, имеют имена в файловой системе), и управление доступом
к ним охватывается файловым механизмом. В современных версиях \sysname{Unix}
адресные пространства исполняющихся процессов также доступны
как файлы в специальной файловой (или псевдофайловой, если угодно)
системе \verb"proc". Файлы в этой ФС могут быть использованы, например,
отладчиками для доступа к коду и данным отлаживаемой программы.
Управление таким доступом также осуществляется стандартным файловым
механизмом.
Кроме доступа к адресному пространству, над процессом в \sysname{Unix}
определена, по существу, только операция посылки сигнала.
Обычный пользователь может посылать сигналы только своим процессам;
только суперпользователь (\verb"root") может посылать их чужим процессам.
Все остальные операции осуществимы только между процессами, связанными
отношением родитель/потомок, и распределение прав доступа в этой ситуации
вообще не нужно. Таким образом, файловые права доступа
используются для управления доступом к практически любым объектам ОС.

В \sysname{Unix System V} появились объекты, не являющиеся файлами
и идентифицируемые численными ключами доступа вместо имен.
Все эти объекты являются средствами межпроцессного
взаимодействия: это семафоры, очереди сообщений и сегменты разделяемой
памяти. Каждый такой объект имеет маску прав доступа, аналогичную
файловой, и доступ к ним контролируется точно так же, как и к файлам.

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

Многие современные системы, не входящие в семейство \sysname{Unix},
а также и некоторые версии \sysname{Unix}, например, \sysname{HP/UX}
или \sysname{SCO UnixWare 2.x}, используют более сложную и гибкую
систему управления доступом, основанную на
\termint{списках управления доступом}{Access Control Lists}{ACL}.
С каждым защищаемым объектом, кроме идентификатора его хозяина,
связан список записей. Каждая запись состоит из идентификатора
пользователя или группы и списка прав для этого пользователя или группы.
Понятие группы в таких системах не играет такой большой роли, как в
\sysname{Unix}, а служит лишь для сокращения ACL, позволяя задать права
для многих пользователей одним элементом списка.
Многие системы, использующие эту модель, например \sysname{Novell Netware},
даже не ассоциируют с файлом идентификатора группы.

Обычно список возможных прав включает в себя право на изменение ACL.
Таким образом, не только хозяин объекта может изменять права доступа к
нему. Это право может быть дано и другим пользователям системы или даже
группам пользователей, если это окажется для чего-то необходимо.

Однако за дополнительную гибкость приходится платить снижением
производительности. В системах семейства \sysname{Unix} проверка прав
доступа осуществляется простой битовой операцией над маской прав.
В системах же, использующих ACL, необходим просмотр списка, который
может занять намного больше времени. Самое плохое состоит в том, что мы не
можем гарантировать завершения этого поиска за какое-либо время, не
устанавливая ограничений на длину списка.

В современных системах накладные расходы, связанные с использованием 
ACL, считаются достаточно малыми, поэтому эта модель распределения
прав приобретает все б\'{о}льшую популярность.

Кроме прав доступа к объектам, система должна управлять выдачей некоторых
привилегий. Так, для выполнения резервного копирования и восстановления
файлов необходим пользователь, способный осуществлять доступ к файлам
и операции на ними, не обращая внимания на права доступа.

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

В большинстве современных многопользовательских ОС, не входящих в
семейство \sysname{Unix}, с каждым пользователем ассоциирован список
привилегий, которыми этот пользователь обладает.
В системах семейства \sysname{Unix} все
гораздо проще: обычные пользователи не обладают никакими привилегиями.
Для выполнения привилегированных функций существует пользователь c
численным идентификатором 0 -- \termin{суперпользователь}{superuser},
который обладает, подобно христианскому Господу Богу, всеми мыслимыми
правами, привилегиями и атрибутами. По традиции суперпользователь
имеет символьное имя \verb"root" -- <<корень>>.

Система списков привилегий предоставляет б\'{о}льшую гибкость,
чем один сверхпривилегированный суперпользователь, потому что позволяет
администратору системы передать часть своих функций, например выполнение
резервного копирования, другим пользователям, не давая им при этом других
привилегий. Однако, как и ACL, эта схема приводит к б\'{о}льшим накладным
расходам при контроле прав доступа: вместо сравнения идентификатора
пользователя с нулем мы должны сканировать список привилегий, что несколько
дольше.

В некоторых ситуациях нужен более тонкий контроль за доступом, чем управление
доступом на уровне файлов. Например, для
изменения информации о пользователе необходим доступ на запись к
соответствующей базе данных, но не ко всей, а только к определенной записи.
Вполне естественно и даже необходимо дать пользователю возможность 
менять пароль, не обращаясь к администратору. С другой стороны, совершенно
недопустима возможность менять пароли других пользователей. Одним из решений
было бы хранение пароля для каждого из пользователей в отдельном файле,
но это во многих отношениях неудобно. Другое решение может состоять в 
использовании модели клиент-сервер с процессом-сервером, исполняющимся с 
привилегиями администратора, который является единственным средством
доступа к паролям. Например, в \sysname{Windows NT} весь доступ к 
пользовательской базе данных осуществляется через системные вызовы, то
есть функции процесса-сервера исполняет само ядро системы. Этот подход
позволяет решить проблему контроля доступа именно к пользовательской
базе данных, но аналогичная проблема возникает и в других ситуациях.

В системах семейства \sysname{Unix} для этой цели был предложен оригинальный
механизм, известный как \termint{setuid}{setting of user id}{установка
[эффективного] идентификатора пользователя}.

{\small
В \sysname{Unix} каждая задача имеет два пользовательских
идентификатора: \term{реальный} и \term{эффективный}. Реальный идентификатор
обычно совпадает с идентификатором пользователя, запустившего задание.
Для проверки прав доступа к файлам и другим объектам, однако, используется
эффективный идентификатор. При запуске обычных задач реальный и эффективный
идентификаторы совпадают. Несовпадение может возникнуть при запуске
программы с установленным признаком \verb"setuid". При этом эффективный
идентификатор для задачи устанавливается равным идентификатору хозяина
файла, содержавшего программу.

\note{Признак \verb"setuid" является атрибутом файла, содержащего загрузочный
модуль программы. Только хозяин может установить этот признак, таким образом
передавая другим пользователям право исполнять эту программу от своего
имени. При модификации файла или при передаче его другому пользователю
признак \verb"setuid" автоматически сбрасывается.}

Так, например, программа \verb"/bin/passwd" принадлежит пользователю
\verb"root", и у нее установлен признак \verb"setuid". Любой пользователь,
запустивший эту программу, получает задачу, имеющую право модификации
пользовательской базы данных -- файлов \verb"/etc/passwd" и
\verb"/etc/shadow". Однако прежде чем произвести модификацию,
программа \verb"passwd" проверяет допустимость модификации.
Например, при смене пароля она требует ввести старый пароль.
Сменить пароль пользователю, который его забыл, может только \verb"root".

Другим примером \verb"setuid"-программы может служить программа
\verb"/bin/ps" (process status -- состояние процессов). Системы семейства
\sysname{Unix} не предоставляют системных вызовов для получения статистики
об исполняющихся процессах. Программа \verb"/bin/ps" анализирует
виртуальную память ядра системы, доступную как файл устройства
\verb"/dev/kmem", находит в ней список процессов и выводит содержащиеся в
списке данные. Естественно, только привилегированная программа может
осуществлять доступ к \verb"/dev/kmem".

Механизм \term{setuid} был изобретен одним из отцов-основателей \sysname{Unix}
Деннисом Ритчи и запатенован фирмой \entname{AT\&T} в 1975 г.
Через несколько месяцев после получения патенту был дан статус public domain.
Официально фирма \entname{AT\&T} объяснила это тем, что плату за
использование данного патента нецелесообразно делать высокой, а сбор
небольшой платы с большого числа пользователей неудобен и тоже
нецелесообразен.
}

Механизм \term{setuid} позволяет выдавать привилегии, доступные только
суперпользователю, как отдельным пользователям (при этом
\verb"setuid"-программа должна явным образом проверять реальный
идентификатор пользователя и сравнивать его с собственной базой данных),
так и группам (при этом достаточно передать \verb"setuid"-программу
соответствующей группе и дать ей право исполнения на эту программу),
таким образом отчасти компенсируя недостаточную гибкость
стандартной системы прав доступа и привилегий в системе \sysname{Unix}.

