BCrypt хэширование

Создано: 20-04-2020

Для написания статьи использованы источники: bcrypt и freecodecamp.

Понимание BCrypt хэширования

Хэш, подобно отпечатку пальца человека, представляет собой отпечаток исходных данных. Хэш - всегда уникальный. Это достигается путем подачи исходных данных в алгоритм и возврата результата фиксированной длины. Чтобы еще больше усложнить этот процесс и сделать его более безопасным, вы также можете посолить свой хэш. Соление хэша включает в себя добавление случайных данных к исходным данным перед процессом хэширования, что делает его еще более трудным для взлома.

BCrypt хэш выглядит так $2a$13$ZyprE5MRw2Q3WpNOGZWGbeG7ADUre1Q8qo.uUUtcbqloU0yvzavOm, и у него есть определенный порядок. Первый кусочек хэша $2a определяет, какой хэш-алгоритм был использован. Следующая часть $13 определяет стоимость. Стоимость - это примерно то, сколько мощности требуется для вычисления хэша. Она находится по логарифмической шкале 2^стоимость и определяет, сколько раз данные проходят через алгоритм хеширования. Например, при стоимости 10 вы можете хэшировать 10 паролей в секунду на среднем компьютере, при стоимости 15 это занимает 3 секунды на хэш..., при стоимости 31 вычисление хэша займет несколько дней. На сегодня, стоимость 12 считается достаточно безопасной. Последняя часть вашего хэша $ZyprE5MRw2Q3WpNOGZWGbeG7ADUre1Q8qo.uUUtcbqloU0yvzavOm, выглядит как одна большая строка цифр, точек и букв, но на самом деле это два отдельных куска информации. Первые 22 символа - это соль в обычном тексте, а остальные - хэшированный пароль.

Чтобы начать использованием bcrypt, сначала установите его.

npm install bcrypt

А затем подключите и определите несколько констант для дальнейших примеров:

const bcrypt = require('bcrypt');

const saltRounds = 10;
const myPlaintextPassword = 's0//P4$$w0rD';
const someOtherPlaintextPassword = 'not_bacon';

Более подробно о node.bcrypt.js читайте здесь.

Хеширование и сравнение паролей асинхронно

Чтобы хэшировать пароль:

Метод 1 (генерация соли и хэша при отдельных вызовах функций):

bcrypt.genSalt(saltRounds, function (err, salt) {
  bcrypt.hash(myPlaintextPassword, salt, function (err, hash) {
    // Храните хэш в своей базе данных паролей.
  });
});

Метод 2 (автогенерация соли и хэша)

bcrypt.hash(myPlaintextPassword, saltRounds, function (err, hash) {
  // Храните хэш в своей базе данных паролей.
});

Обратите внимание, что оба метода дают один и тот же конечный результат.

Чтобы проверить пароль:

// Загрузите хэш из вашей базы данных паролей.
bcrypt.compare(myPlaintextPassword, hash, function (err, result) {
  // result == true
});
bcrypt.compare(someOtherPlaintextPassword, hash, function (err, result) {
  // result == false
});

Почему асинхронный режим рекомендуется использовать вместо синхронного режима? Если вы используете bcrypt на простом скрипте, то использование синхронного режима совершенно нормально. Однако, если вы используете bcrypt на сервере, рекомендуется использовать асинхронный режим. Это связано с тем, что хэширование, выполняемое bcrypt, требует больших затрат процессора, поэтому синхронная версия заблокирует цикл событий и не позволит вашему приложению обслуживать любые другие входящие запросы или события. Асинхронная версия использует пул потоков, который не блокирует основной цикл событий.

Поскольку хэширование предназначено для интенсивных вычислений, рекомендуется делать это асинхронно на вашем сервере, чтобы избежать блокировки входящих соединений во время хэширования. Чтобы хэшировать пароль асинхронно, нужно выполнить

bcrypt.hash(myPlaintextPassword, saltRounds, (err, hash) => {
  /* Храните хэш в базе данных */
  console.log(hash);
});

Теперь, когда вам нужно выяснить, является ли новый ввод теми же данными, что и хэш, вы просто используете функцию сравнения.

bcrypt.compare(myPlaintextPassword, hash, (err, res) => {
  /*res == true or false*/
});

Добавьте это в существующую хэш-функцию (поскольку вам нужно дождаться завершения хэша перед вызовом функции сравнения) после того, как вы выведите завершенный хэш и выведите "res" в консоль внутри compare. Вы должны увидеть в консоли хэш, а затем напечатать "true"! Если вы измените 'myPlaintextPassword 'в функции сравнения на' какой-то другой пароль обычного текста', то он должен сказать false.

bcrypt.hash('passw0rd!', 13, (err, hash) => {
  console.log(hash);
  //$2a$12$Y.PHPE15wR25qrrtgGkiYe2sXo98cjuMCG1YwSI5rJW1DSJp0gEYS
  bcrypt.compare('passw0rd!', hash, (err, res) => {
    console.log(res); //true
  });
});

Хэширование и сравнение паролей синхронно

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

var hash = bcrypt.hashSync(myPlaintextPassword, saltRounds);

Теперь, чтобы сравнить ввод пароля с новым хэшем, вы должны использовать метод compareSync:

var result = bcrypt.compareSync(myPlaintextPassword, hash);

в результате получается логическое значение true или false.