0

"SALT и HASH паролей в Node.js с использованием crypto"

14

Проблема с валидацией пароля после хеширования в Node.js с использованием модуля crypto

Я пытаюсь реализовать механизм хеширования и соль для паролей в Node.js, используя модуль crypto. У меня успешно получается создать хешированный пароль следующим образом:

UserSchema.pre('save', function(next) {
  var user = this;

  var salt = crypto.randomBytes(128).toString('base64');
  crypto.pbkdf2(user.password, salt, 10000, 512, function(err, derivedKey) {
    user.password = derivedKey;
    user.salt = salt; // Сохраняю соль для дальнейшего использования
    next();
  });
});

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

Вот мой текущий метод для проверки пароля:

UserSchema.methods.validPassword = function(password) {
  // здесь нужно взять соль и выполнить хеширование пароля для сравнения
  // как мне получить соль?
}

Помогите, пожалуйста, понять, как правильно осуществить валидацию пароля после его хеширования.

3 ответ(ов)

0

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

Чтобы проверить введенный пароль, вам нужно будет сравнить его с хешем: вы берете новый открытый пароль, хешируете его с использованием той же соли (и количества итераций), а затем сравниваете полученную последовательность байтов с сохраненной.

Для генерации пароля (псевдокод)

function hashPassword(password) {
    var salt = crypto.randomBytes(128).toString('base64'); // Генерация соли
    var iterations = 10000; // Количество итераций
    var hash = pbkdf2(password, salt, iterations); // Хеширование пароля

    return {
        salt: salt,
        hash: hash,
        iterations: iterations
    };
}

Для проверки пароля (псевдокод)

function isPasswordCorrect(savedHash, savedSalt, savedIterations, passwordAttempt) {
    return savedHash == pbkdf2(passwordAttempt, savedSalt, savedIterations); // Сравнение хешей
}

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

0

Вот модифицированная версия ответа @Matthews с использованием TypeScript на вопрос о хэшировании паролей:

import * as crypto from "crypto";

const PASSWORD_LENGTH = 256; // длина генерируемого пароля
const SALT_LENGTH = 64; // длина соли
const ITERATIONS = 10000; // количество итераций
const DIGEST = "sha256"; // алгоритм хэширования
const BYTE_TO_STRING_ENCODING = "hex"; // кодировка: может быть и base64, например

/**
 * Интерфейс, содержащий информацию о пароле, которая хранится в базе данных
 */
interface PersistedPassword {
  salt: string;
  hash: string;
  iterations: number;
}

/**
 * Генерирует PersistedPassword на основе пароля, предоставленного пользователем.
 * Эта функция должна вызываться при создании пользователя или изменении пароля.
 */
export function generateHashPassword(
  password: string
): Promise<PersistedPassword> {
  return new Promise<PersistedPassword>((accept, reject) => {
    const salt = crypto
      .randomBytes(SALT_LENGTH)
      .toString(BYTE_TO_STRING_ENCODING);
    
    crypto.pbkdf2(
      password,
      salt,
      ITERATIONS,
      PASSWORD_LENGTH,
      DIGEST,
      (error, hash) => {
        if (error) {
          return reject(error);
        }

        accept({
          salt,
          hash: hash.toString(BYTE_TO_STRING_ENCODING),
          iterations: ITERATIONS,
        });
      }
    );
  });
}

/**
 * Проверяет, соответствует ли введённый пароль информации о пароле, сохраненной в
 * базе данных. Эта функция должна вызываться, когда пользователь пытается войти в систему.
 */
export function verifyPassword(
  persistedPassword: PersistedPassword,
  passwordAttempt: string
): Promise<boolean> {
  return new Promise<boolean>((accept, reject) => {
    crypto.pbkdf2(
      passwordAttempt,
      persistedPassword.salt,
      persistedPassword.iterations,
      PASSWORD_LENGTH,
      DIGEST,
      (error, hash) => {
        if (error) {
          return reject(error);
        }

        accept(
          persistedPassword.hash === hash.toString(BYTE_TO_STRING_ENCODING)
        );
      }
    );
  });
}

В этом коде реализованы функции для генерации и проверки паролей с использованием алгоритма PBKDF2 и SHA-256. Первая функция generateHashPassword генерирует соль и хэш для заданного пароля, а вторая функция verifyPassword проверяет, соответствует ли введённый пароль сохранённым данным. Убедитесь, что вы используете безопасные методы при хранении паролей в вашей базе данных.

0

Столкнувшись с такой же задачей, я собрал все в одном модуле: https://www.npmjs.org/package/password-hash-and-salt.

Он использует pbkdf2 и хранит хеш, соль, алгоритм и количество итераций в одном поле. Надеюсь, это поможет!

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь