На запрос “Enter password:” введем любой пришедший на ум пароль. Например, “MyGoodPassword”. Запустив “passwd.simple.exe” убедимся: программа уверенно распознает правильные и ложные пароли, работая на первый взгляд как будто безупречно.
К сожалению, такую защиту очень легко обойти. Если есть доступ к файлу паролей (а если его не будет, как изволите извлекать пароли для проверки?) тривиальный просмотр содержимого позволит получить пароли всех пользователей, а это никак не входит в планы разработчиков защиты.
Для разминки воспользуемся командой “type passwd.simple” и на экране незамедлительно появится содержимое файла паролей:
· type passwd.simple
· MyGoodPassword
Разработчики UNIX нашли оригинальное решение, прибегнув к необратимому преобразованию - хешированию. Предположим, существует некая функция f, преобразующая исходную строку к некой последовательности байт таким образом, чтобы обратный процесс был невозможен или требовал огромного объема вычислений, заведомо недоступного злоумышленнику. Математички это можно записать как:
· f(passwd) -» x
Вся изюминка в том, что если строка s1 равна строке s2, то и f(s1) заведомо равно f(s2). А это дает возможность отказаться от хранения паролей в открытом виде. В самом деле, вместо этого можно сохранить значение функции f(passwd), где passwd оригинальный пароль. Затем применить ту же хеш-функцию к паролю, введенному пользователем (userpasswd), и, если f(userpasswd) - f(passwd), то и userpasswd - passwd! Поэтому, доступность файла «зашифрованных» паролей уже не позволяет воспользоваться ими в корыстных целях, поскольку по условию функция f гарантирует, что результат ее вычислений необратим, и исходный пароль найти невозможно.
Сказанное легче понять, самостоятельно написав простейшую программу, работающую по описанной выше методике. Сначала необходимо выбрать функцию преобразования, отвечающую указанным условиям. К сожалению, это сложная задача, требующая глубоких познаний криптографии и математики, поэтому, просто посчитаем сумму ASCII кодов символов пароля и запомним результат. Пример реализации такого алгоритма приведен ниже (смотри файл “passwd.add.new.user.c”):
Запустим откомпилированный пример на выполнение и в качестве пароля введем, например, “MyGoodPassword”. Теперь напишем программу, проверяющую вводимый пользователем пароль. Один из возможных вариантов реализации показан ниже (смотри файл “/SRC/passwd.c”):
Обратите внимание на выделенные строки - и в том, и в другом случае использовалась одна и та же функция преобразования. Убедившись в умении программы отличать «свои» пароли от «чужих», заглянем в файл “passwd”, отдав команду “type passwd”:
Совсем другое дело! Попробуй-ка, теперь угадай, какой пароль необходимо ввести, что система его пропустила. Строго говоря, в приведенном примере это можно без труда и задача решается едва ли не в уме, но условимся считать выбранную функцию односторонней и необратимой.
Из односторонности функции следует невозможность восстановления оригинального пароля по его хешу, и доступность файла passwd уже не позволит злоумышленнику проникнуть в систему. Расшифровать пароли невозможно, потому что их там нет. Другой вопрос, удастся ли подобрать некую строку, воспринимаемую системой как правильный пароль? К обсуждению этого вопроса мы еще вернемся позже, а для начала рассмотрим устройство механизма аутентификации в UNIX.
Первые версии UNIX в качестве односторонней функции использовали модифицированный вариант известного криптостойкого алгоритма DES. Под криптостойкостью в данном случае понимается гарантированная невозможность вычисления подходящего пароля никаким иными способом, кроме тупого перебора. Впрочем, существовали платы, реализующие такой перебор на аппаратном уровне и вскрывающие систему за разумное время, поэтому пришлось пойти рискованный шаг, внося некоторые изменения в алгоритм, «ослепляющие» существующее «железо». Риск заключался в возможной потере криптостойкости и необратимости функции. К счастью, этого не произошло.
Другая проблема заключалась в совпадении паролей пользователей. В самом деле, если два человека выберут себе одинаковые пароли, то и хеши этих паролей окажутся одинаковыми (а как же иначе?). А вот это никуда не годится, - в многопользовательской системе шансы подобного совпадения не так малы, как это может показаться на первый взгляд, и в результате возможен несанкционированный доступ к чужим ресурсам.
Разработчики нашли элегантное решение, - результат операции хеширования зависит не только от введенного пароля, но и случайной последовательности бит, называемой привязкой (salt). Разумеется, саму привязку после хеширования необходимо сохранять, иначе процесс не удастся обратить. Однако никакого секрета привязка не представляет, (поскольку шифрует не хеш-сумму, а вводимый пользователем пароль).
Хеш-суммы, привязки и некоторая другая информация в UNIX обычно хранится в файле “/etc/passwd”, состоящего из строк следующего вида:
· kpnc:z3c24adf310s:16:13:Kris Kaspersky:/home/kpnc:/bin/bash
Разберем, что этот дремучий лес обозначает. Первым идет имя пользователя (например, “kpnc”), за ним, отделенное двое точением, следует то, что начинающими по незнанию называется «зашифрованным паролем». На самом деле это никакой не пароль - первые два символа представляют собой сохраненную привязку, а остаток - необратимое преобразование от пароля, то есть хеш. Затем (отделенные двоеточием) идут номер пользователя и номер группы, дополнительная информация о пользователе (как правило, полное имя), а замыкают строй домашний каталог пользователя и оболочка, запускаемая по умолчанию.
Шифрование паролей происходит следующим образом, - случайным образом выбираются два символа привязки [100], использующиеся для модификации алгоритма DES. Затем шифруется строка пробелов с использованием пароля в качестве ключа. Полученное 64 битое значение преобразуется в одинадцатисимвольную строку. Спереди к ней дописываются два символа привязки, и на этом весь процесс заканчивается.
Продемонстрировать работу функции crypt поможет следующий пример (на диске он расположен в файле “/SRC/ctypt.c”). Его компиляция потребует библиотеки ast.lib, распространяемой вместе с “UWIN” (смотри главу «Как запускать UNIX приложения на Windows»), если же такой библиотеки у читателя нет, можно воспользоваться готовым к работе файлом “/SRC/crypt.exe”. Для запуска программы в командной строке необходимо указать шифруемый пароль и отделенную пробелом привязку.