Реализация криптохэша – Node.js 22 – Angular 18

Я уже искал решения в Stack Overflow и Google, но ни одно из решений не работает, я всегда получаю следующую ошибку:

Error: Module not found: Error: Can't resolve 'crypto' in ....

Я уже сделал:

  • npm update
  • ng update

auth.service.ts: (не компонент html — не в браузере — скрытая служба аутентификации)

import { Injectable } from '@angular/core';
import { scrypt, randomBytes, timingSafeEqual } from "crypto";

const keyLength = 32;

@Injectable({
  providedIn: 'root'
})
export class AuthService {

constructor() {};

async login( username: string, password: string ) {
  if ( await AuthService.compare(username, await AuthService.hash(password))) {
    localStorage.setItem('STATE', 'true');
    return true;
  }

  return false;
}
  
async logout() {
    localStorage.setItem('STATE', 'false');

    return true;
}
  
isLoggedIn() {
  const loggedIn = localStorage.getItem('STATE');
  if (loggedIn == 'true')
    return true;
  else
    return false;
}

/**
 * Has a password or a secret with a password hashing algorithm (scrypt)
 * @param {string} password
 * @returns {string} The salt+hash
 */
static async hash( password: string ): Promise<string> {
  return new Promise((resolve, reject) => {
      // generate random 16 bytes long salt - recommended by NodeJS Docs
      const salt = randomBytes(16).toString("hex");

      scrypt(password, salt, keyLength, (err, derivedKey) => {
          if (err) reject(err);
          // derivedKey is of type Buffer
          resolve(`${salt}.${derivedKey.toString("hex")}`);
      });
    });
  };

/**
* Compare a plain text password with a salt+hash password
* @param {string} password The plain text password
* @param {string} hash The hash+salt to check against
* @returns {boolean}
*/
static async compare( password: string, hash: string ): Promise<boolean> {
  return new Promise((resolve, reject) => {
      const [salt, hashKey] = hash.split(".");
      // we need to pass buffer values to timingSafeEqual
      const hashKeyBuff = Buffer.from(hashKey, "hex");
      scrypt(password, salt, keyLength, (err, derivedKey) => {
          if (err) reject(err);
          // compare the new supplied password with the hashed password using timeSafeEqual
          resolve(timingSafeEqual(hashKeyBuff, derivedKey));
      });
    });
  };

}

Модуль шифрования встроен в Node.js 22.4.1, который я использую для своего Angular 18: https://nodejs.org/api/crypto.html#cryptoscryptpassword-salt-keylen-options-callback

Решение не в следующем:

  • npm install crypto (старый способ)
  • не использую Crypto-JS
  • В коде Visual Studio ошибки не отображаются (но ошибки появляются при выполнении «Начать отладку»)

Этот фрагмент кода выполняется на стороне сервера? Вероятно, нет, в браузере у вас нет чего-то вроде криптомодуля, но у вас есть Developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API, который также доступен в Node и может быть полезен. выбор.

stepeusz 14.07.2024 23:47

Кроме того, узел, начиная с версии 15, поддерживает API веб-криптографии. nodejs.org/api/crypto.html#cryptowebcrypto

President James K. Polk 15.07.2024 00:48
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Angular и React для вашего проекта веб-разработки?
Angular и React для вашего проекта веб-разработки?
Когда дело доходит до веб-разработки, выбор правильного front-end фреймворка имеет решающее значение. Angular и React - два самых популярных...
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Мы провели Twitter Space, обсудив несколько проблем, связанных с последними дополнениями в Angular. Также прошла Angular Tiny Conf с 25 докладами.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Мое недавнее углубление в Angular
Мое недавнее углубление в Angular
Недавно я провел некоторое время, изучая фреймворк Angular, и я хотел поделиться своим опытом со всеми вами. Как человек, который любит глубоко...
Освоение Observables и Subjects в Rxjs:
Освоение Observables и Subjects в Rxjs:
Давайте начнем с основ и постепенно перейдем к более продвинутым концепциям в RxJS в Angular
0
2
70
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вам нужно будет использовать что-то доступное в браузерах, например Web Crypto API.

Хеширование может выглядеть примерно так:

static async hash(password: string): Promise<string> {
  const salt = crypto.getRandomValues(new Uint8Array(16))
  const key = await crypto.subtle.importKey(
    "raw",
    new TextEncoder().encode(password),
    { name: "PBKDF2" },
    false,
    ["deriveBits"])
  const derivedBits = await crypto.subtle.deriveBits({
    name: "PBKDF2",
    salt,
    iterations: 100000,
    hash: "SHA-256"
  }, key, 256)
  return `${this.bufferToHex(salt)}.${this.bufferToHex(derivedBits)}`
}

Сравнение может выглядеть примерно так:

static async compare(password: string, hash: string): Promise<boolean> {
  const [saltHex, storedHashHex] = hash.split('.')
  const key = await crypto.subtle.importKey(
    "raw",
    new TextEncoder().encode(password),
    { name: "PBKDF2" },
    false,
    ["deriveBits"]
  )
  const derivedBits = await crypto.subtle.deriveBits({
    name: "PBKDF2",
    salt: this.hexToBuffer(saltHex),
    iterations: 100000,
    hash: "SHA-256"
  }, key, 256)
  return storedHashHex === this.bufferToHex(derivedBits)
}

Для того, чтобы это сработало, не нужно import ничего делать. Я не эксперт по безопасности, поэтому не могу гарантировать безопасность этих алгоритмов, но PBKDF2 обычно считается приемлемым для хеширования паролей.

Другие вопросы по теме