Пишем собственную утилиту для генерации паролей

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

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

. Существует проблема придумывания/генерации сложного пароля, хотя сегодня для этого имеется достаточное количество утилит (Sologen, например).
. Чем сложнее пароль, тем сложнее его запомнить. Разумеется, на запоминание 25-символьного пароля, состоящего из всех печатаемых символов в разном начертании, потребуется очень много времени, да и забыть его элементарно просто без отсутствия постоянной тренировки. Ну, а если не запоминать пассворд, то…
. Возникает проблема хранения хорошего пароля. Даже если потратили много сил на придумывание сложной алфавитно-цифровой комбинации, встает закономерный вопрос: где ее сохранить? В текстовом файле? Довольно глупо, потому что вся сложность пароля сводится на нет простотой его обнаружения. Можно записать пароль на бумажку и положить рядом с монитором, однако это тоже ненадежно, если кто-то, кроме вас, имеет доступ к компьютеру. К тому же, на набор длинного пассворда каждый раз придется тратить много времени. Существуют еще специальные программы для сохранения паролей, которые помещают наш пассворд в специальный зашифрованный стойким алгоритмом контейнер, обеспечивая тем самым его относительную сохранность, однако криптография не стоит на месте, и рано или поздно он может быть взломан. Впрочем, последний вариант самый надежный, поэтому советую использовать только его (бесплатную Digital Identity 1.0.20, например).

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

Аспекты генерации псевдослучайных чисел (ГПСЧ)

Для того чтобы пароль мог считаться надежным, он должен состоять из случайных символов и, соответственно, генерироваться с помощью случайной числовой последовательности. Замечу, что по-настоящему случайного генератора в природе не существует: есть только менее однородные и более однородные модули генерации. Чем больше однородность генератора, тем более случайные числа он будет генерировать, т.е. будет лучше. Для работы генератора случайных чисел необходимо инициализирующее его число (криптографы называют это источником энтропии) либо ряд чисел, т.к. по внутреннему строению ГПСЧ это ряд сложных математических функций. Таким инициализирующим параметром может быть системное время, различные параметры оборудования либо даже перемещение мыши. Генератор случайных чисел имеется практически в каждой среде программирования — например, rand(); в C или random(); в Pascal/Delphi. Однако не всегда встроенный генератор может быть хорошим: например, в PHP функция rand(); генерирует из рук вон плохие последовательности — правда, там есть еще mt_rand();, работающая значительно лучше. Существуют также аппаратные генераторы, создающие очень хорошие последовательности со скоростью Мб/с и служащие основой шифровальных устройств (такими генераторами славится, например, VIA). Подробнее о ГПСЧ вы можете прочитать во врезке. Теперь переходим к основному вопросу — выбору среды разработки. Представленный ниже алгоритм довольно прост, поэтому реализовать его можно будет на любом языке программирования, и первоочередным критерием для вас должно быть удобство реализации дополнительной функциональности вроде старт/стоп/сохранить и т.д. Немного подумав, я выбрал Delphi как наиболее простую и доступную среду.

Ядро утилиты

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

procedure generate;
var
i,j,r,num,pl: integer;
s,p:string;
begin
s:='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+.\|@#%*&/{}[]<>,';
p:='';
Randomize; //инициализация генератора
for i := 1 to num do begin //num — количество генерируемых паролей
for j:= 1 to pl do begin //pl — длина одного пароля
r:=random(length(s)); //получаем случайный символ
if r=0 then r:=1;
p:=p+s[r]; //"накручиваем" переменную p до нужной длины
end;
form1.Memo1.Lines.Add(p);//добавляем пароли на memo
p:='';
end;
end;

В Delphi random(); инициализируется при помощи команды Randomize;, которая, в свою очередь, использует системное время для работы (функция _time();). Скажу сразу, что использование одного лишь random'а, тем более стандартного, — простейший вариант. Разумеется, никто не мешает усложнить эту процедуру с помощью получения идентификатора сессии пользователя (seed) и работы с ней, а также чтения всевозможных идентификаторов оборудования из реестра и работы с ними. Например, у меня в перспективе создание селективного варианта, т.е. с возможностью выбора строки, от которой производить генерацию. В любом случае подобрать даже тот пароль, который наша программа создает сейчас, будет очень трудно при его длине хотя бы в 10 символов.

Внешний вид и дополнительная функциональность

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

num:=strtoint(Edit1.text);
pl:=strtoint(Edit2.text);

Далее нужно предусмотреть возможность остановки процесса генерации, если пользователь создает большое количество паролей. Для этого заведем логическую переменную halt и добавим кнопку на форму. В процедуру generate добавим: halt:=false;, а на нажатие кнопки — halt:=true;. Теперь в цикле нашей основной функции будем осуществлять проверку:

if halt=true then break;

Уверен, что вы без труда доделаете функциональность старт/пауза. Теперь добавляем еще две кнопки на форму и диалог сохранения (SaveDialog). На одну вешаем очистку memo:

Memo1.Text:='';

На вторую — сохранение результата:

if SaveDialog1.Execute=true then Memo1.Lines.SaveToFile(SaveDialog1.FileName+'.txt');

В Object Inspector'е в свойствах диалога сохранения нужно настроить параметр Filter: Filter=Text Files|*.txt. После этого при открытии диалога нам будет предложено сразу сохранять файл в текстовом формате. Также стоит реализовать вывод информации о процессе генерирования, т.е. количество уже созданных паролей, скорость генерирования и расчетный объем файла. Добавим три компонента Label и два таймера на форму. Заведем также четыре глобальные переменные типа integer: speed, size, col, delta. Первая будет служить для расчета скорости, вторая — для размера, третья же будет содержать общее число созданных паролей, а четвертая — временной интервал с момента включения таймера. На первый таймер вешаем следующий код:

var n:integer;
begin
n:=col;
inc(delta);
speed:=round(n/(delta*(timer1.Interval/1000)));//рассчитываем среднюю скорость путем деления числа созданных паролей на время с момента начала.
size:=round(((strtoint(Edit2.text)+2)*n)/1024); //здесь +2 означает учет двух символов переноса строки при расчете размера.
end;

На второй — вот это:
Label1.Caption:=inttostr(speed);
Label2.Caption:=inttostr(size);

И модифицируем процедуру generate, полный листинг которой приведен ниже:

procedure generate;
var
i,j,r,num,pl: integer;
s,p:string;
begin
s:='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+.\|@#%*&/{}[]<>,';
p:='';
delta:=0; //обнуляем временную переменную при каждом запуске функции
num:=strtoint(Edit1.text);
pl:=strtoint(Edit2.text);
Timer1.Enabled:=true; //включаем оба таймера
Timer2.Enabled:=true;
halt:=false;
Randomize;
for i := 1 to num do begin
for j:= 1 to pl do begin
r:=random(length(s));
if r=0 then r:=1;
p:=p+s[r];
end;
Memo1.Lines.Add(p);
Application.ProcessMessages; //эта строчка нужна, чтобы наше приложение реагировало на действия пользователя. Это хоть и несколько замедляет работу цикла, но является очевидной необходимостью.
if halt=true then break;//функция Стоп.
col:=i; //число созданных паролей равно числу итераций цикла
Label3.Caption:=inttostr(i);
p:='';
end;
timer1.Enabled:=false; //выключаем оба таймера
timer2.Enabled:=false;
end;

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

Заключение

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

Для справки: ГПСЧ

Генератор псевдослучайных чисел (ГПСЧ) — это алгоритм генерации ряда чисел, элементы которого практически независимы друг от друга, и их последовательность невозможно отличить от случайной. Никакой арифметический ГПСЧ не может генерировать полностью случайные числа — только лишь аппроксимировать некоторые свойства случайных чисел. Как сказал Джон фон Нейман, "всякий, кто питает слабость к арифметическим методам получения случайных чисел, грешен вне всяких сомнений". Любой арифметический ГПСЧ с ограниченными ресурсами рано или поздно зацикливается. Длина циклов ГПСЧ зависит от самого генератора и находится в пределах 2(n/2) до 2n, где n — размер внутреннего состояния в битах. Большинство простых арифметических генераторов хотя и обладают большой скоростью, но страдают от многих серьезных недостатков:

. Слишком короткий период/периоды.
. Последовательные значения не являются независимыми.
. Некоторые числа менее случайны, чем другие.
. Неравномерное одномерное распределение.
. Обратимость.

В частности, алгоритм RANDU — десятилетиями использовавшийся на компьютерах IBM мейнфрейм — оказался очень плохим. В результате многие исследования менее надежны, чем могли бы быть, т.к. ГПСЧ широко используются в имитационном моделировании. Из современных ГПСЧ широкое распространение получил "виток Мерсенна", предложенный в 1997 году Мацумото и Нишимурой. Его достоинствами являются колоссальный период (219937- 1), равномерное распределение в 623 измерениях (линейный конгруэнтный метод дает более или менее равномерное распределение от силы в 5 измерениях), быстрая генерация случайных чисел (в 2-3 раза быстрее, чем стандартные ГПСЧ, использующие линейный конгруэнтный метод). Однако существуют сложные алгоритмы, распознающие последовательность, порождаемую с помощью "витка Мерсенна", как неслучайную. Это делает "виток Мерсенна" неподходящим для криптографии. Существуют и криптостойкие генераторы, и они предлагают гораздо более случайные случайные числа, но такие ГПСЧ гораздо медленнее, чем обычные арифметические, и могут быть непригодны во всякого рода исследованиях, требующих, чтобы процессор был свободен для более полезных вычислений.

В военных и научных целях, где существует необходимость генерировать совершенно непредсказуемые или попросту абсолютно случайные числа, используются криптостойкие ГПСЧ с внешним источником энтропии. ГПСЧ с источником энтропии способен генерировать значительно более случайные последовательности или даже абсолютно случайные (тогда его уже можно называть ГСЧ). В персональных компьютерах авторы программных генераторов используют такие источники энтропии, как шум звуковой карты или значения счетчика тактов процессора, которые легко считываются, например, при помощи инструкции rdtsc в процессорах Intel. До появления в процессорах возможности считывать значение самого чувствительного к малейшим изменениям окружающей среды счетчика тактов процессора сбор энтропии являлся наиболее уязвимым местом ГПСЧ. Эта проблема до сих пор полностью не разрешена во многих устройствах (например, smart-карты), которые таким образом остаются уязвимыми. Многие ГПСЧ до сих пор используют традиционные (устаревшие) методы сбора энтропии — к примеру, действия пользователя (движения мыши и т.п.), как, например в PGP и Yarrow, или взаимодействие между программными потоками, как, например, в Java Secure Random. Вот несколько примеров ГПСЧ с их источниками энтропии и генераторами:

. /dev/random в UNIX/Linux — источник энтропии: счетчик тактов процессора плюс хэширование выхода через SHA-1; достоинства: есть во всех Unix'ах, надежный источник энтропии; недостатки: очень долго нагревается, может надолго застревать.
. Yarrow от Брюса Шнайера — источник энтропии: традиционные (устаревшие) методы плюс хэширование выхода через AES-256; достоинства: гибкий криптостойкий дизайн; недостатки: долго нагревается, очень маленькое внутреннее состояние, слишком сильно зависит от криптостойкости выбранных алгоритмов, медленный, применим исключительно для генерации ключей.
Генератор от Леонида Юрьева (Leo Yuriev) — источник энтропии: шум звуковой карты; достоинства: скорее всего, хороший и быстрый источник энтропии; доступен исключительно в виде DLL под Windows.
. Microsoft CryptoAPI — источник энтропии: текущее время, размер жесткого диска, размер свободной памяти, ID-процесса и NETBIOS имя компьютера; достоинства: встроен в Windows, не застревает; недостатки: маленькое внутреннее состояние, легкопредсказуем.
. Java Secure Random — источник энтропии: взаимодействие между программными потоками; достоинства — в Java другого выбора пока нет, большое внутреннее состояние; недостатки: медленный сбор энтропии, хотя в Java другого выбора пока все равно нет.
. Chaos от Ruptor — источник энтропии: счетчик тактов процессора, собирается непрерывно; достоинства: пока самый быстрый из всех, большое внутреннее состояние, не застревает.
Источник данных: wikipedia.org

Алексей Голованов, AlekseyGolovanov@mail.ru


Компьютерная газета. Статья была опубликована в номере 05 за 2007 год в рубрике безопасность

©1997-2024 Компьютерная газета