it-swarm.com.de

SALT- und HASH-Passwort in nodejs w/crypto

Ich versuche herauszufinden, wie man ein Passwort in nodejs mit dem Kryptomodul salzt und hasht. Ich kann das Hash-Passwort auf diese Weise erstellen:

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

Ich bin jedoch verwirrt, wie ich das Passwort später validieren kann.

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

Unabhängig davon, welchen Persistenzmechanismus (Datenbank) Sie verwenden, würden Sie den resultierenden Hash neben dem Salt-Wert und der Anzahl der Iterationen speichern, die beide Klartext wären. Wenn für jedes Kennwort unterschiedliche Salt-Werte verwendet werden (was Sie tun sollten), müssen Sie diese Informationen ebenfalls speichern.

Sie würden dann das neue Klartextpasswort vergleichen, das Hash mit dem gleichen Salt (und denselben Iterationen) hashieren, und dann die Bytesequenz mit der gespeicherten vergleichen.

So generieren Sie das Passwort (Pseudo)

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
    };
}

Passwort bestätigen (Pseudo)

function isPasswordCorrect(savedHash, savedSalt, savedIterations, passwordAttempt) {
    return savedHash == pbkdf2(passwordAttempt, savedSalt, savedIterations);
}
40
Matthew

Basierend auf der Nodejs-Dokumentation ( http://nodejs.org/api/crypto.html ) sieht es nicht so aus, als gäbe es eine bestimmte Methode, die ein Kennwort für Sie validiert. Um es manuell zu bestätigen, müssen Sie den Hashwert des aktuell bereitgestellten Kennworts berechnen und mit dem gespeicherten auf Gleichheit vergleichen. Im Grunde machen Sie dasselbe mit dem Challenge-Passwort, das Sie mit dem Original gemacht haben. Verwenden Sie jedoch das in der Datenbank gespeicherte Salt, anstatt ein neues zu generieren, und vergleichen Sie dann die beiden Hashes.

Wenn Sie sich nicht zu sehr für die Verwendung der integrierten Kryptobibliothek entscheiden, kann ich stattdessen die Verwendung von bcrypt empfehlen. Die beiden sind in Bezug auf die Sicherheit in etwa gleich, aber ich denke, dass bcrypt eine benutzerfreundlichere Oberfläche hat. Ein Beispiel für die Verwendung (direkt aus den bcrypt-Dokumenten auf der oben verlinkten Seite entnommen) wäre:

Einen Hash erstellen:

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

So prüfen Sie ein Passwort:

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

Bearbeiten, um hinzuzufügen:

Ein weiterer Vorteil von bcrypt besteht darin, dass die -Ausgabe der genSalt-Funktion sowohl den Hash als auch den Salt in einer Zeichenfolge enthält. Das bedeutet, dass Sie nur ein einzelnes Element in Ihrer Datenbank speichern können, anstatt zwei. Es gibt auch eine Methode, mit der ein Salt zur gleichen Zeit generiert wird, zu der das Hashing stattfindet. Sie müssen sich also keine Gedanken darüber machen, wie das Salt verwaltet wird.

Zum Aktualisieren bearbeiten:

Antwort auf den Kommentar von Peter Lyons: Sie sind zu 100% korrekt. Ich hatte angenommen, dass das von mir empfohlene bcrypt-Modul eine Javascript-Implementierung war. Daher würde die asynchrone Verwendung des Knotens Single-Threaded-Modell die Sache nicht wirklich beschleunigen. Es stellt sich heraus, dass dies nicht der Fall ist. Das bcrypt-Modul verwendet für die Berechnungen nativen C++ - Code und läuft asynchron schneller. Peter Lyons hat recht, Sie sollten zuerst die asynchrone Version der Methode verwenden und nur bei Bedarf die synchrone auswählen. Die asynchrone Methode Vielleicht ist so langsam wie die synchrone Methode, aber die synchrone Methode wird immer langsam sein.

24
TwentyMiles

Speichern Sie entweder das Kennwort und das Salt in separaten Spalten in Ihrer Datenbank oder (meine bevorzugte Methode), und speichern Sie Ihre Kennwörter in einem Format, das mit RFC 2307 Abschnitt 5.3 kompatibel ist. Ein Beispiel wäre {X-PBKDF2}base64salt:base64digest. Sie können auch die Anzahl der Iterationen dort speichern, sodass Sie zukünftig die Anzahl der Iterationen für neue Konten und Konten, die Ihre Kennwörter aktualisieren, erhöhen kann, ohne die Anmeldungen für alle anderen Benutzer zu beeinträchtigen.

Ein Beispielhash aus meinem eigenen PBKDF2-Modul für Perl sieht so aus
{X-PBKDF2}HMACSHA1:AAAD6A:8ODUPA==:1HSdSVVwlWSZhbPGO7GIZ4iUbrk=, der den spezifischen verwendeten Hash-Algorithmus sowie die Anzahl der Iterationen, den Salt-Wert und den resultierenden Schlüssel enthält.

8
hobbs

Angesichts der gleichen Frage habe ich alles in einem Modul zusammengefasst: https://www.npmjs.org/package/password-hash-and-salt

Es verwendet pbkdf2 und speichert Hash, Salt, Algorithmus und Iterationen in einem einzigen Feld. Ich hoffe es hilft.

6
florian

Ich denke, dieses Tutorial wäre für Sie am besten geeignet. Gehen Sie einfach durch, es ist das Beste, was ich bisher gefunden habe . Passport-Tutorial mit Node.js und Crypto

Hoffe du findest es hilfreich.

5

Dies ist eine modifizierte Version von @Matthews answer, die TypeScript verwendet

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

Dieses Szenario umfasst zwei Hauptschritte

1) Passwort erstellen und speichern

Hier müssen Sie Folgendes tun.

  • Nimm das Benutzerpasswort
  • Erzeugen Sie eine Folge von zufälligen Zeichen (Salt)
  • Kombinieren Sie das Salt mit dem vom Benutzer eingegebenen Passwort
  • Hash die kombinierte Zeichenfolge.
  • Speichern Sie den Hash und das Salt in der Datenbank.

2) Benutzerpasswort überprüfen

Dieser Schritt wäre erforderlich, um den Benutzer zu authentifizieren.

  • Der Benutzer gibt den Benutzernamen/die E-Mail-Adresse und das Passwort ein.

  • Rufen Sie den Hash und das Salt anhand des eingegebenen Benutzernamens ab

  • Kombinieren Sie das Salt mit dem Benutzerpasswort

  • Hash die Kombination mit dem gleichen Hash-Algorithmus.

  • Vergleichen Sie das Ergebnis.

In diesem Lernprogramm wird ausführlich erläutert, wie mit nodejs crypto gearbeitet wird. Genau das, wonach Sie suchen. Salt-Hash-Passwörter mit NodeJS-Verschlüsselung

1
rahil471