Прокачиваем собственный генератор паролей

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

В осно­ве гене­ра­то­ра лежал алго­ритм хеши­ро­ва­ния — когда любую стро­ку мож­но зашиф­ро­вать так, что­бы потом невоз­мож­но было вос­ста­но­вить исход­ный текст. Даёшь ему любой текст, а он тебе что-то такое: 88A42764A5ADE6FF2B27D2981A58A24A — вот это и было осно­вой паро­ля.

У наше­го гене­ра­то­ра был боль­шой потен­ци­ал для роста и дора­бо­ток. Сего­дня мы доба­вим в гене­ра­тор новые воз­мож­но­сти и повы­сим без­опас­ность алго­рит­ма. Вот что мы сде­ла­ем:

За осно­ву возь­мём исход­ный код из про­шлой ста­тьи.

Исходный код

<!DOCTYPE html>
<html>
<!-- служебная часть -->

<head>
  <!-- заголовок страницы -->
  <title>Генератор паролей</title>
  <!-- настраиваем служебную информацию для браузеров -->
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- подключаем функцию, которая хеширует строку -->
  <script src="http://thecode.local/wp-content/uploads/2019/05/js-md5.js"></script>
  <!-- задаём CSS-стили прямо здесь же, чтобы всё было в одном файле -->
  <style type="text/css">
    /*задаём общие параметры для всей страницы: шрифт и отступы*/
    body {
      text-align: center;
      margin: 10;
      font-family: Verdana, Arial, sans-serif;
      font-size: 16px;
    }

    /* настраиваем внешний вид полей ввода*/
    input {
      display: inline-block;
      margin: 20px auto;
      border: 2px solid #eee;
      padding: 10px 20px;
      font-family: Verdana, Arial, sans-serif;
      font-size: 16px;
    }

    /*закончили со стилями*/
  </style>
  <!-- закрываем служебную часть страницы -->
</head>

<body>
  <!-- началась визуальная часть -->
  <!-- ставим всё содержимое в один блок по центру  -->
  <div class="container" margin: auto;">
    <!-- заголовок страницы -->
    <h2 class="todo__caption">Генератор паролей</h2>
    <!--делаем отступ -->
    <br>
    <!-- задаём поле ввода для адреса нужного сайта и настраиваем параметры поля -->
    <input type="text" id="site_url" size="50" placeholder="Вставьте адрес сайта, где вам нужен пароль">
    <!--делаем отступ -->
    <br>
    <!-- задаём поле ввода для кодового слова и настраиваем параметры поля -->
    <input type="text" id="keyword" size="50" placeholder="Напишите кодовое слово, чтобы сделать пароль сильнее">
    <!--делаем двойной отступ -->
    <br>
    <br>
    <!-- вставляем кнопку, которая запускает генератор пароля и настраиваем отступы -->
    <button style="font-size: 100%; padding:5px 10px 10px 10px" onclick="generate()">Создать пароль</button>
    <!--выводим сгенерированный пароль -->
    <p>Ваш пароль:</p>
    <div id="pass_text" style="font-weight: bold"></div>
  </div>
  <!-- закончилась видимая часть -->
  <!-- пишем скрипт, который будет генерировать пароль по нажатию кнопки -->
  <script>
    // задаём переменные, где будем хранить адрес сайта, кодовое слово…
    var site, salt;
    // …исходные текстовые данные для пароля и сам пароль
    var text, password;
    // объявляем функцию, которая создаёт пароль и выводит его на экран
    function generate() {
      // кладём в соответствующие переменные текстовое значение адреса сайта…
      site = document.getElementById('site_url').value;
      // …и кодового слова
      salt = document.getElementById('keyword').value;
      // подготавливаем исходную строку для пароля и добавляем в неё наш секретный ингредиент
      text = site + salt + 'Misha Polyanin molodets';
      // на основе этой строки с помощью функции md5, которую мы подключили в самом начале, создаём пароль
      password = md5(text);
      // выводим его на экран
      document.getElementById('pass_text').innerHTML = password;
    }
  // закончилась скриптовая часть
  </script>
</body>
<!-- конец всей страницы -->

</html>

Прокачиваем внешний вид генератора

Сра­зу сде­ла­ем всё в визу­аль­ной части стра­ни­цы, что­бы потом к ней не воз­вра­щать­ся. 

Нам нуж­но доба­вить поле, куда поль­зо­ва­тель будет вво­дить сек­рет­ное чис­ло. Рабо­тать оно будет так: берём пароль, раз­би­ва­ем его на груп­пы по три сим­во­ла, берём сек­рет­ное чис­ло и, начи­ная с него, берём три груп­пы сим­во­лов. Меж­ду груп­па­ми встав­ля­ем дефис, что­бы полу­чил­ся пароль вида abc-def-ghj. Его лег­че запом­нить, к тому же он зави­сит от сек­рет­но­го чис­ла: новое чис­ло — новый пароль.

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

Вто­рое добав­ле­ние — галоч­ка, кото­рая пока­зы­ва­ет, нуж­но нам исполь­зо­вать в паро­ле спец­сим­во­лы (%:*#@%&) или нет. Логи­ка про­стая: галоч­ка сто­ит — добав­ля­ем спец­сим­во­лы в пароль.

<!-- задаём числовое поле ввода для секретного числа -->
<input type="number" id="secnumber" size="50" placeholder="Введите число">
<!--делаем отступ -->
<br>
<!-- добавляем чекбокс, который скажет нам, нужны спецсимволы или нет -->
<input type="checkbox" id="sym">Использовать спецсимволы
<!--делаем отступ -->
<br>

Сохра­ня­ем изме­не­ния и обнов­ля­ем стра­ни­цу с гене­ра­то­ром:

Готовим скрипт

Мы будем обра­щать­ся к эле­мен­там по их име­нам, а зна­чит, нам нуж­но под­клю­чить jQuery:

<!-- подключаем JQuery --> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"> </script>

Затем немно­го изме­ним наш блок с пере­мен­ны­ми, доба­вим, чего не хва­та­ет, что­бы этот блок выгля­дел так:

// задаём переменные, где будем хранить адрес сайта, кодовое слово, секретное число… var site, salt, secnum; // …исходные текстовые данные для пароля и сам пароль var text, password; // переменная для временного хранения исходного пароля var sourcepass; // спецсимволы, которые добавим в пароль, если нужно var symbols; symbols = '%:*#@%&';

Сразу выдаём пароль

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

// отслеживаем каждое нажатие клавиши при вводе секретной фразы и при каждом нажатии генерируем пароль
document.getElementById('keyword').addEventListener('keydown', function (e) {
  generate();
})

А теперь поду­май­те: как пове­сить такой же обра­бот­чик собы­тий на поле с чис­лом и на поле с галоч­кой?

Прокачиваем сам генератор

Раз мы вво­дим сек­рет­ное чис­ло, надо сра­зу его обра­бо­тать и запом­нить в отдель­ной пере­мен­ной. Это чис­ло вли­я­ет на выбор началь­ной трой­ки для паро­ля и не может быть мень­ше еди­ни­цы:

// вводим секретное число secnum = document.getElementById('secnumber').value; // нельзя стартовать с нулевой тройки чисел или меньше if (secnum < 1) {secnum = 1};

Раз мы будем моди­фи­ци­ро­вать пароль, кото­рый в самом нача­ле полу­ча­ем с помо­щью MD5-функции, то сохра­ним его в дру­гой пере­мен­ной. На её осно­ве мы полу­чим нуж­ный нам моди­фи­ци­ро­ван­ный пароль. Заод­но очи­стим пере­мен­ную с финаль­ным паро­лем:

// на основе этой строки с помощью функции md5, которую мы подключили в самом начале, создаём пароль sourcepass = md5(text); // обнуляем переменную, где будет храниться изменённый пароль password = '';

Наста­ло вре­мя доба­вить боль­шие бук­вы и спец­сим­во­лы. Что­бы выбрать, какие бук­вы делать боль­ши­ми, мы возь­мём оста­ток от деле­ния наше­го сек­рет­но­го чис­ла на 3 и полу­чен­ное чис­ло при­мем за шаг. Как толь­ко дохо­дим до ново­го шага — дела­ем бук­ву боль­шой. Со спец­сим­во­ла­ми тоже всё про­сто: каж­дый раз про­ве­ря­ем, сто­ит галоч­ка или нет. Если сто­ит — добав­ля­ем их на каж­дой тре­тьей ите­ра­ции цик­ла. Выбор спец­сим­во­ла тоже зави­сит от сек­рет­но­го чис­ла.

// цикл, который отвечает за большие числа и спецсимволы в пароле, проходит всю длину строки и обрабатывает каждый символ
for (var i = 0; i < sourcepass.length; i++) {
  // секретным числом определяем шаг, с которым будем делать следующую заглавную букву в пароле
  var upperstep = secnum % 3;
  // если шаг сработал — делаем большой символ, иначе оставляем старый
  if (i % upperstep == 0) { password += sourcepass[i].toUpperCase() }
  else { password += sourcepass[i] };
  // если поставлена галочка про спецсимволы…
  if (document.getElementById('sym').checked) {
    // на каждой третьей итерации вставляем спецсимвол на нужное место
    if (i % 3 == 0) { password += symbols[secnum % 6] }
  }
} 

Разбиваем пароль на тройки

Здесь вся слож­ность в том, что­бы преду­смот­реть такой вари­ант: сек­рет­ное чис­ло боль­ше, чем коли­че­ство тро­ек в паро­ле. В этом слу­чае мы по кру­гу про­хо­дим все трой­ки столь­ко раз, сколь­ко нуж­но пока не закон­чит­ся сек­рет­ное чис­ло, и начи­на­ем с теку­щей пози­ции. Если трой­ки закон­чат­ся в про­цес­се — вер­нём­ся в нача­ло и будем брать их отту­да. Весь код добав­ля­ем в ту же функ­цию generate():

// перед тем как мысленно разбивать пароль на тройки, поместим его в промежуточную переменную
sourcepass = password;
// обнуляем строку — в неё будем заново собирать наш пароль из троек
password = '';
// если наше секретное число больше, чем групп из трёх символов в исходном пароле — прогоняем тройки по кругу, возвращаемся в начало и останавливаемся на нужной позиции
if (Number(secnum) * 3 > sourcepass.length) { secnum = (Number(secnum) * 3) % sourcepass.length } else { secnum = secnum * 3 };
// формируем пароль из групп по три символа с разделителем
for (var i = 1; i < 10; i++) {
  // если после сложения мы превысили длину исходного пароля — возвращаемся в начало, чтобы продолжить на следующем шаге с первого
  if (sourcepass.length == secnum + i) { secnum = -i };
  // добавляем очередной символ в группу из трёх символов
  password += sourcepass[secnum + i];
  // если прошло уже три символа — добавляем разделитель (а после девятого символа — не добавляем)
  if ((i % 3 == 0) && (i % 9 != 0)) { password += '-' };
} 

Соби­ра­ем весь код вме­сте и смот­рим, что полу­чи­лось:

Готовый код

<!DOCTYPE html>
<html>
<!-- служебная часть -->

<head>
  <!-- заголовок страницы -->
  <title>Генератор паролей</title>
  <!-- настраиваем служебную информацию для браузеров -->
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- подключаем функцию, которая хеширует строку -->
  <script src="http://thecode.local/wp-content/uploads/2019/05/js-md5.js"></script>
  <!-- задаём CSS-стили прямо здесь же, чтобы всё было в одном файле -->
  <style type="text/css">
    /*задаём общие параметры для всей страницы: шрифт и отступы*/
    body {
      text-align: center;
      margin: 10;
      font-family: Verdana, Arial, sans-serif;
      font-size: 16px;
    }

    /* настраиваем внешний вид полей ввода*/
    input {
      display: inline-block;
      margin: 20px auto;
      border: 2px solid #eee;
      padding: 10px 20px;
      font-family: Verdana, Arial, sans-serif;
      font-size: 16px;
    }

    /*закончили со стилями*/
  </style>
  <!-- закрываем служебную часть страницы -->
</head>

<body>
  <!-- началась визуальная часть -->
  <!-- ставим всё содержимое в один блок по центру  -->
  <div class="container" margin: auto;">
    <!-- заголовок страницы -->
    <h2 class="todo__caption">Генератор паролей</h2>
    <!--делаем отступ -->
    <br>
    <!-- задаём поле ввода для адреса нужного сайта и настраиваем параметры поля -->
    <input type="text" id="site_url" size="50" placeholder="Вставьте адрес сайта, где вам нужен пароль">
    <!--делаем отступ -->
    <br>
    <!-- задаём поле ввода для кодового слова и настраиваем параметры поля -->
    <input type="text" id="keyword" size="50" placeholder="Напишите кодовое слово, чтобы сделать пароль сильнее">
    <!--делаем отступ -->
    <br>
    <!-- задаём числовое поле ввода для секретного числа -->
    <input type="number" id="secnumber" size="50" placeholder="Введите число">
    <!--делаем отступ -->
    <br>
    <!-- добавляем чекбокс, который скажет нам, нужны спецсимволы или нет -->
    <input type="checkbox" id="sym">Использовать спецсимволы
    <!--делаем отступ -->
    <br>
    <!--делаем двойной отступ -->
    <br>
    <br>
    <!-- вставляем кнопку, которая запускает генератор пароля и настраиваем отступы -->
    <button style="font-size: 100%; padding:5px 10px 10px 10px" onclick="generate()">Создать пароль</button>
    <!--выводим сгенерированный пароль -->
    <p>Ваш пароль:</p>
    <div id="pass_text" style="font-weight: bold"></div>
  </div>
  <!-- закончилась видимая часть -->
  <!-- подключаем JQuery -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js">
  </script>
  <!-- пишем скрипт, который будет генерировать пароль по нажатию кнопки -->
  <script>
    // отслеживаем каждое нажатие клавиши при вводе секретной фразы и при каждом нажатии генерируем пароль
    document.getElementById('keyword').addEventListener('keydown', function (e) {
      generate();
    })
    // функция, которая возвращает случайное число в заданном диапазоне
    function randz(min, max) {
      return Math.floor(Math.random() * (max - min + 1)) + min;
    }
    // задаём переменные, где будем хранить адрес сайта, кодовое слово, секретное число…
    var site, salt, secnum;
    // …исходные текстовые данные для пароля и сам пароль
    var text, password;
    // переменная для временного хранения исходного пароля
    var sourcepass;
    // спецсимволы, которые добавим в пароль, если нужно
    var symbols;
    symbols = '%:*#@%&';
    // объявляем функцию, которая создаёт пароль и выводит его на экран
    function generate() {
      // кладём в соответствующие переменные текстовое значение адреса сайта,
      site = document.getElementById('site_url').value;
      // кодового слова…
      salt = document.getElementById('keyword').value;
      // и секретного числа…
      secnum = document.getElementById('secnumber').value;
      // нельзя стартовать с нулевой тройки чисел или меньше
      if (secnum < 1) { secnum = 1 };
      // подготавливаем исходную строку для пароля и добавляем в неё наш секретный ингредиент
      text = site + salt + 'Misha Polyanin molodets';
      // на основе этой строки с помощью функции md5, которую мы подключили в самом начале, создаём пароль
      sourcepass = md5(text);
      // обнуляем переменную, где будет храниться изменённый пароль
      password = '';
      // цикл, который отвечает за большие числа и спецсимволы в пароле, проходит всю длину строки и обрабатывает каждый символ
      for (var i = 0; i < sourcepass.length; i++) {
        // секретным числом определяем шаг, с которым будем делать следующую заглавную букву в пароле
        var upperstep = secnum % 3;
        // если шаг сработал — делаем большой символ, иначе оставляем старый
        if (i % upperstep == 0) { password += sourcepass[i].toUpperCase() }
        else { password += sourcepass[i] };
        // если поставлена галочка про спецсимволы…
        if (document.getElementById('sym').checked) {
          // на каждой третьей итерации вставляем спецсимвол на нужное место
          if (i % 3 == 0) { password += symbols[secnum % 6] }
        }
      }
      // перед тем как мысленно разбивать пароль на тройки, поместим его в промежуточную переменную
      sourcepass = password;
      // обнуляем строку — в неё будем заново собирать наш пароль из троек
      password = '';
      // если наше секретное число больше, чем групп из трёх символов в исходном пароле — прогоняем тройки по кругу, возвращаемся в начало и останавливаемся на нужной позиции
      if (Number(secnum) * 3 > sourcepass.length) { secnum = (Number(secnum) * 3) % sourcepass.length } else { secnum = secnum * 3 };
      // формируем пароль из групп по три символа с разделителем
      for (var i = 1; i < 10; i++) {
        // если после сложения мы превысили длину исходного пароля — возвращаемся в начало, чтобы продолжить на следующем шаге с первого
        if (sourcepass.length == secnum + i) { secnum = -i };
        // добавляем очередной символ в группу из трёх символов
        password += sourcepass[secnum + i];
        // если прошло уже три символа — добавляем разделитель (а после девятого символа — не добавляем)
        if ((i % 3 == 0) && (i % 9 != 0)) { password += '-' };
      }
      // выводим финальный пароль
      document.getElementById('pass_text').innerHTML = password;
    }
  // закончилась скриптовая часть
  </script>
</body>
<!-- конец всей страницы -->

</html>

В финаль­ной ста­тье мы сде­ла­ем из этой стра­ни­цы отдель­ное при­ло­же­ние, кото­рое мож­но запу­стить на ком­пью­те­ре или на теле­фоне! Под­пи­сы­вай­тесь, что­бы не про­пу­стить.