web-dev-qa-db-ja.com

nodejs w / cryptのSALTおよびHASHパスワード

私は暗号化モジュールを使用してnodejsでパスワードをソルトおよびハッシュする方法を見つけようとしています。これを実行して、ハッシュ化されたパスワードを作成できます。

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;
    next();
  });
});

ただし、後でパスワードを検証する方法について混乱しています。

UserSchema.methods.validPassword = function(password) {    
  // need to salt and hash this password I think to compare
  // how to I get the salt?
}
27

使用している永続化メカニズム(データベース)に関係なく、結果のハッシュをソルトと反復回数(両方ともプレーンテキスト)と共に保存します。各パスワードが異なるソルトを使用する場合(実行する必要があります)、その情報も保存する必要があります。

次に、新しいプレーンテキストパスワードを比較し、同じソルト(および反復)を使用してハッシュし、次にバイトシーケンスを保存されているものと比較します。

パスワード(擬似)を生成するには

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);
}
42
Matthew

Nodejsのドキュメント( http://nodejs.org/api/crypto.html )に基づくと、パスワードを検証する特定の方法があるようには見えません。手動で検証するには、現在提供されているパスワードのハッシュを計算し、保存されているパスワードと同等かどうかを比較する必要があります。基本的に、元のパスワードと同じことをチャレンジパスワードで行いますが、新しいパスワードを生成する代わりにデータベースに保存されているソルトを使用し、2つのハッシュを比較します。

組み込みの暗号ライブラリの使用にあまりコミットしていない場合は、代わりに bcrypt を使用することをお勧めします。セキュリティの面ではこの2つはほぼ同じですが、bcryptの方がユーザーフレンドリーなインターフェイスだと思います。使用方法の例(上記のリンクページのbcryptドキュメントから直接取得)は次のようになります。

ハッシュを作成します。

var bcrypt = require('bcrypt');
var salt = bcrypt.genSaltSync(10);
var hash = bcrypt.hashSync("B4c0/\/", salt);
// Store hash in your password DB.

パスワードを確認するには:

// Load hash from your password DB.
bcrypt.compareSync("B4c0/\/", hash); // true
bcrypt.compareSync("not_bacon", hash); // false

追加して編集:

Bcryptのもう1つの利点は、 genSalt関数の出力に1つの文字列にハッシュとソルトの両方が含まれる であることです。これは、2つではなく、1つのアイテムのみをデータベースに保存できることを意味します。ハッシュが発生すると同時にソルトを生成するメソッドも用意されているため、ソルトの管理をまったく心配する必要はありません。

編集して更新:

Peter Lyonsからのコメントに対する回答:あなたは100%正しいです。私がお勧めしたbcryptモジュールはjavascript実装であると想定していたため、非同期で使用すると、ノードのシングルスレッドモデルでの処理が実際には高速化されません。これは事実ではないことが判明しました。 bcryptモジュールは、計算にネイティブc ++コードを使用し、非同期でより高速に実行されます。 Peter Lyonsが正しい。最初に非同期バージョンのメソッドを使用し、必要な場合にのみ同期バージョンを選択する必要があります。非同期メソッドmightは同期メソッドと同じくらい遅くなりますが、同期メソッドは常にalways遅くなります。

24
TwentyMiles

パスワードとソルトをデータベースの別々の列に保存するか、(私の好みの方法) RFC 2307 セクション5.3と互換性のある形式でデータベースにパスワードを保存します。例は{X-PBKDF2}base64salt:base64digest。そこに繰り返し回数を保存することもできます。これにより、他のすべてのユーザーのログインを中断することなく、新しいアカウントとパスワードを更新するアカウントの将来の繰り返し回数を増やすことができます。

私自身の Perl用PBKDF2モジュール のハッシュの例は次のようになります
{X-PBKDF2}HMACSHA1:AAAD6A:8ODUPA==:1HSdSVVwlWSZhbPGO7GIZ4iUbrk=これには、使用された特定のハッシュアルゴリズム、反復回数、ソルト、および結果のキーが含まれます。

9
hobbs

同じ質問に直面して、すべてを1つのモジュールにまとめました: https://www.npmjs.org/package/password-hash-and-salt

Pbkdf2を使用し、ハッシュ、ソルト、アルゴリズム、および反復を単一のフィールドに保存します。それが役に立てば幸い。

6
florian

このチュートリアルが最も適していると思います。ちょうどそれを通過してください、それは私がまだ見つけた最高です。 Node.jsとCryptoを使用したパスポートチュートリアル

お役に立てば幸いです。

5

これは、TypeScriptを使用した@Matthews回答の修正版です

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'; // this could be base64, for instance

/**
 * The information about the password that is stored in the database
 */
interface PersistedPassword {
    salt: string;
    hash: string;
    iterations: number;
}

/**
 * Generates a PersistedPassword given the password provided by the user. This should be called when creating a user
 * or redefining the password
 */
export async 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) {
                reject(error);
            } else {
                accept({
                    salt,
                    hash: hash.toString(BYTE_TO_STRING_ENCODING),
                    iterations: ITERATIONS,
                });
            }
        });
    });
}

/**
 * Verifies the attempted password against the password information saved in the database. This should be called when
 * the user tries to log in.
 */
export async 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) {
                reject(error);
            } else {
                accept(persistedPassword.hash === hash.toString(BYTE_TO_STRING_ENCODING));
            }
        });
    });
}
4
André Pena

このシナリオには2つの主要な手順が含まれます

1)パスワードの作成と保存

ここでは、次のことを行う必要があります。

  • ユーザーパスワードを取得する
  • ランダムな文字列(塩)を生成します
  • ソルトとユーザーが入力したパスワードを組み合わせる
  • 結合された文字列をハッシュします。
  • ハッシュとソルトをデータベースに保存します。

2)ユーザーパスワードの検証

ユーザーを認証するには、この手順が必要です。

  • ユーザーはユーザー名/メールアドレスとパスワードを入力します。

  • 入力したユーザー名に基づいてハッシュとソルトを取得します

  • Saltとユーザーパスワードを組み合わせます

  • 同じハッシュアルゴリズムで組み合わせをハッシュします。

  • 結果を比較します。

このチュートリアルでは、nodejs暗号を使用してそれを行う方法について詳しく説明しています。まさにあなたが探しているもの。 NodeJS暗号化を使用したソルトハッシュパスワード

1
rahil471