Пример генерации паролей средствами JavaScript

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

Работа генератора паролей

Генерация псевдослучайных последовательностей символов, в том числе паролей, — стандартная задача, которая достаточно просто реализуется средствами 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], то предстоит проделать три операции:

  1. Получить псевдослучайное число от 0 до 1.
  2. Умножить псевдослучайное число на 3.
  3. Округлить псевдослучайное число (до целочисленного значения).

Для первой операции используем вызов Math.random() и получаем, например, число 0.6795051876197733. Во время второй операции при умножении его на 3 получаем 2.03851556285932. Для третьего действия задействуем метод Math.floor, который округляет число до ближайшего меньшего целочисленного значения, и мы получаем 2.

Соответственно, если из массива имён ["Екатерина", "Ольга", "Анна"] надо выбрать случайным образом победительницу лотереи подписчиц сайта веб-разработчиков SchoolsW3, то ей станет... Ольга, поскольку именно это имя имеет в массиве индекс, равный 2.

А теперь посмотрим, как реализован алгоритм генерации пароля. У нас длина пароля должна быть 8 символов, при этом имеются три обязательных условия:

  1. Первый символ пароля — всегда буква латинского алфавита (прописная или строчная).
  2. Следующие 6 символов могут быть либо буквами в любом регистре, либо цифрами 0-9, либо знаками "-" и "_".
  3. Последний символ — всегда либо буква (в любом регистре), либо цифра.

Сначала мы задаем три строковых константы. Первая содержит все буквы латиницы в обеих регистрах (всего 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);.

На самом деле мы схитрили. Если перейти к странице генерации пароля, то новый пароль будет получен не при нажатии на одноименную кнопку "Новый пароль", а при загрузке страницы; по этой кнопке он будет выведен пользователю. Это сделано для того, чтобы сгенерированный пароль не сбрасывался, пока страница не обновлена. За счет такого решения его можно при необходимости вызвать снова и скопировать в буфер обмена. Лишь при нажатии кнопки "Перезагрузить" происходит перезагрузка страницы и генерация новой псевдослучайной последовательности. Конечно, у подобного подхода есть свои минусы, но для демонстрации он нам вполне подходит.

Надёжность генерируемых паролей

Криптографический метод RandomSource.getRandomValues()

Необходимо иметь в виду, что метод генерации псевдослучайного набора символов с использованием Math.random() не предоставляет криптографически стойкие случайные последовательности, поэтому его не рекомендуется использовать в случаях, связанных с безопасностью информационных систем. Из-за особенностей реализации данного метода в ограниченной выборке генерируемых последовательностей вероятны их повторения. Более детально о проблемах при использовании метода Math.random() можно прочитать в статье TIFU by using Math.random().

Тем не менее, такие псевдослучайные пароли более устойчивы, чем простые и мнемонически легкие последовательности, создаваемые самими пользователями (не только пароли наподобие 12345678, password, но и любые комбинации с именем пользователя и датой его рождения и т.д.). Поэтому в некоторых случаях их можно использовать: например, когда надо быстро зарегистрировать некоторое (ограниченнное) количество пользователей и выдать им привязанные к их логинам пароли, причем, хотя мы и можем в этом случае проверять, генерировалась ли уже у нас подобная последовательность символов, её повторение не будет критичным (логины-то у всех уникальные, например, e-mail); к тому же пользователю можно предлагать самостоятельно сменить пароль после первого захода на Веб-сервис.

Для целей криптографии рекомендуется использовать другой метод генерации псевдослучайных чисел с использованием RandomSource.getRandomValues() (подробнее на сайте разработчиков Mozilla.org).