Генератор псевдослучайных паролей

Генерация псевдослучайных последовательностей символов, в том числе паролей, — стандартная задача, которая достаточно просто реализуется средствами JavaScript. Для этого не нужны никакие дополнительные программы. Пользователь может сгенерировать отвечающий определенным критериям пароль (длина пароля, допустимый набор символов и их сочетание) прямо в браузере и тут же использовать его на соответсвующем Веб-сервере.
По ссылке можно посмотреть, как действует один из вариантов реализации генератора паролей. О том, почему применяемый метод генерирует не случайные, а псевдослучайные последовательности, рассказывается ниже.
Реализация генератора паролей при помощи метода Math.random()
Скрипт для генерации паролей находится в файле generator.js.
Для создания пароля применяемся метод Math.random()
,
являющийся генератором псевдослучайных чисел. "Псевдослучайными"
полученные последовательности являются, так как лежащие в их основе
числа (да, последовательность символов для формирования опирается на
числа) не являются истинно случайными. Истинно случайные числа не
взаимосвязаны между собой; генератор же псевдослучайных чисел
опирается на некоторое единственное "начальное
зерно" — начальное значение, которое дальше
используется для расчёта; поэтому получается последовательность
взаимозависимых чисел (обычно отвечающих определенному
распределению), которая только внешне кажется случайной. Кроме того,
такие генераторы имеют свойство зацикливаться, отчего одна и та же
последовательность может повториться на отрезке, существенном
меньшем теоретически возможного диапазона генерируемых
последовательностей.
Метод Math.random()
генерирует псевдослучайное число
от 0
включительно до 1
(не включая её),
например, такие числа:
0.0015467539673197361
.
0.13235970565393496
0.22651388806144368
0.3905677668590535
0.46759905129094004
0.5960284982021409
0.6149564759337036
0.776173016722481
0.8413439304484869
0.9639323381700424
Для удобства я подобрал числа для
всех цифр после запятой и расположил
их в порядке возрастания, но в реальности числа генерируются
вразброс, и число, начинающееся на 0.7
, я получил лишь
после двух десятков обращений к этому методу.
Если нам надо случайным образом выбрать из массива, включающего 3
числа [0, 1, 2]
, то предстоит проделать три операции:
- Получить псевдослучайное число от
0
до1
. - Умножить псевдослучайное число на
3
. - Округлить псевдослучайное число (до целочисленного значения).
Для первой операции используем вызов Math.random()
и
получаем, например, число 0.6795051876197733
. Во время
второй операции при умножении его на 3
получаем 2.03851556285932
.
Для третьего действия задействуем метод Math.floor
,
который округляет число до ближайшего меньшего целочисленного
значения, и мы получаем 2
.
Соответственно, если из массива имён ["Екатерина",
"Ольга", "Анна"]
надо выбрать случайным
образом победительницу лотереи подписчиц сайта веб-разработчиков SchoolsW3, то ей станет...
Ольга
, поскольку именно это имя имеет в массиве индекс,
равный 2
.
А теперь посмотрим, как реализован алгоритм генерации пароля. У нас длина пароля должна быть 8 символов, при этом имеются три обязательных условия:
- Первый символ пароля — всегда буква латинского алфавита (прописная или строчная).
- Следующие 6 символов могут быть либо буквами в любом регистре, либо цифрами 0-9, либо знаками "-" и "_".
- Последний символ — всегда либо буква (в любом регистре), либо цифра.
Сначала мы задаем три строковых константы. Первая содержит все буквы латиницы в обеих регистрах (всего 52 символа), её мы назовем ABC. Вторая константа NUMBERS включает только 10 чисел (от 0 до 9 включительно), третья SIGNS — 2 знака "-" и "_". Поскольку для второго условия нам подойдут все последовательности, объединим их в новую константу SYMBOLS (получится 64 доступных символа). А для третьего условия объединим только буквы и цифры в константу LETTERS (62 символа).
Также зададим переменную password для будущего пароля: она пока символов содержать не будет.
const ABC = "AaBbCcDdEeFfGgHhIiJiKkLlMmNnJjPpQqRrSsTtUuVvWwXxYyZz";
const NUMBERS = "0123456789";
const SIGNS = "-_";
const SYMBOLS = ABC + NUMBERS + SIGNS;
const LETTERS = ABC + NUMBERS;
let password = "";
Далее используем для генерации пароля функцию
getPassword()
с аргументами в виде последовательностей
символов. На первом этапе данная функция заполнит первый символ
пароля, псевдослучайно выбрав его по индексу из строки букв
ABC. На втором этапе в 6 проходов
псевдослучайно будут выбраны и занесены в пароль символы из строки
SYMBOLS. На третьем этапе для выбора будет использована
строка LETTERS, которая даст нам последний символ (букву
или цифру).
function getPassword (FIRST, SECOND, THIRD) {
password += FIRST[Math.floor(Math.random() * FIRST.length)];
for (let i = 0; i < 6; i++) {
password +=
SECONS[Math.floor(Math.random() * SECOND.length)];
}
password += THIRD[Math.floor(Math.random() * THIRD.length)];
return password;
}
Теперь можно вызвать функцию, передав ей в качестве аргументов имена
последовательностей (в нужном порядке): getPassword(ABC,
SYMBOLS, LETTERS);
.
На самом деле мы схитрили. Если перейти к странице генерации пароля, то новый пароль будет получен не при нажатии на одноименную кнопку "Новый пароль", а при загрузке страницы; по этой кнопке он будет выведен пользователю. Это сделано для того, чтобы сгенерированный пароль не сбрасывался, пока страница не обновлена. За счет такого решения его можно при необходимости вызвать снова и скопировать в буфер обмена. Лишь при нажатии кнопки "Перезагрузить" происходит перезагрузка страницы и генерация новой псевдослучайной последовательности. Конечно, у подобного подхода есть свои минусы, но для демонстрации он нам вполне подходит.
Надёжность генерируемых паролей

Необходимо иметь в виду, что метод генерации псевдослучайного набора
символов с использованием Math.random()
не
предоставляет
криптографически стойкие случайные последовательности, поэтому его
не рекомендуется использовать в случаях, связанных с безопасностью
информационных систем. Из-за особенностей реализации данного метода
в ограниченной выборке генерируемых последовательностей вероятны их
повторения. Более детально о проблемах при использовании метода
Math.random()
можно прочитать в статье TIFU by using Math.random().
Тем не менее, такие псевдослучайные пароли более устойчивы, чем простые и мнемонически легкие последовательности, создаваемые самими пользователями (не только пароли наподобие 12345678, password, но и любые комбинации с именем пользователя и датой его рождения и т.д.). Поэтому в некоторых случаях их можно использовать: например, когда надо быстро зарегистрировать некоторое (ограниченнное) количество пользователей и выдать им привязанные к их логинам пароли, причем, хотя мы и можем в этом случае проверять, генерировалась ли уже у нас подобная последовательность символов, её повторение не будет критичным (логины-то у всех уникальные, например, e-mail); к тому же пользователю можно предлагать самостоятельно сменить пароль после первого захода на Веб-сервис.
Для целей криптографии рекомендуется использовать другой метод
генерации псевдослучайных чисел с использованием RandomSource.getRandomValues()
(подробнее на сайте разработчиков Mozilla.org).