我如何使用 Node.js 验证由 Python 的 Passlib 创建的加密哈希?

How can I verify a cryptographic hash created by Python's Passlib using Node.js?

我有一个用 Python 编写的后端应用程序,供我网站的内容管理员使用。用户的密码使用 passlib's pbkdf2_sha512 函数进行哈希处理。我开始开发前端应用程序,出于用户体验的原因,我决定将 nodejs 与 React 结合使用。

现在我的问题是我不知道如何使用 nodejs 验证 passlib 散列的密码来验证我的用户。 Passlib 的实现对我来说似乎太具体了,我不太喜欢加密的东西。

我有 MCF,所以我知道算法和摘要类型、盐、迭代次数和密钥长度。如何验证节点中 passlib 的输出?我应该选择另一种两个平台都更好支持的算法吗?

好的,我转向 sha512_crypt 并找到了一个名为 sha512crypt-node 的不错的节点库。自述文件本身包含 Python 和 Node 的示例,这正是我所需要的。这是给 ppl 的一个小例子。使用这些平台:

Python:

from passlib.hash import sha512_crypt

orig = "password"
h = sha512_crypt.encrypt(orig)
print("hash", h)
# h for eg. is $rounds=100000YnTXatKh4b1pLjpQQjVIfjrbiTakj.wkaw1woAcFiPRAjJP2U/b3BiGW4m8OvI8x0tgw1bb63dNQWMUl1uYNDBcTO3tWgrJ6eHh1

okay = sha512_crypt.verify(orig, h)
print("verified", okay)

节点:

var sha512crypt = require("sha512crypt-node").sha512crypt;

// origHash is the hash generated by passlib    
var origHash = "$rounds=100000YnTXatKh4b1pLjpQQjVIfjrbiTakj.wkaw1woAcFiPRAjJP2U/b3BiGW4m8OvI8x0tgw1bb63dNQWMUl1uYNDBcTO3tWgrJ6eHh1",
    parts = origHash.split('$'),
    rounds = parts[2],
    salt = '$' + parts[1] + '$' + rounds + '$' + parts[3],
    password = "password";

var hash = sha512crypt(password, salt);
console.log("verified", hash === origHash);

我们遇到了完全相同的问题,但无法切换到 sha512crypt。在我们的例子中,密码是使用 flask-security 生成的。下面的示例涵盖了常规的 passlib 和 flask-security 哈希,它们首先生成一个带有秘密盐的 HMAC,并将其用作 pbkdf2-sha512 密码。请参阅下面的代码。

源已打开 GitHub:https://github.com/badzong/node-verify-flask-security-passwords

var crypto = require('crypto');
var pbkdf2_sha512 = require('pbkdf2-sha512');

function b64trimmed(buf) {
        return buf.toString('base64').replace(/=*$/, '').replace('+', '.');
}

function b64decode(str) {
        // . in Base64?
        str = str.replace('.', '+');
        if (str.length % 4) {
                str += '='.repeat(4 - str.length % 4);
        }
        return new Buffer(str, 'base64');
}

function get_hmac(secret, password) {
        var hmac = crypto.createHmac('sha512', secret).update(password).digest('base64');

        return hmac;
}

function get_hash(password, salt, rounds) {

        // FIXME: KeyLenBytes is hardcoded
        var h = b64trimmed(pbkdf2_sha512(password, salt, rounds, 64));
        var joined_hash = ['', 'pbkdf2-sha512', rounds, b64trimmed(salt), h].join('$');

        return joined_hash;
}

function verify_hash(password, stored_hash) {
        var scheme = stored_hash.split('$')[1];
        var rounds = stored_hash.split('$')[2];
        var salt = stored_hash.split('$')[3];

        // FIXME: Maybe throw an exception
        if (scheme !== 'pbkdf2-sha512') {
                return false;
        }

        var h = get_hash(password, b64decode(salt), rounds);

        return h === stored_hash;
}

function new_hash(password, rounds) {

        // FIXME: Salt size is hardcoded
        var salt = crypto.randomBytes(16);

        return get_hash(password, salt, rounds);
}

var password = 'Example Password';

// Usage:
var h = new_hash(password, 20000);
console.log('HASH ' + h);
console.log('VERIFY ' + verify_hash(password, h));

// Usage for passwords generated with flask_security:

// SECURITY_PASSWORD_SALT is set in config.py and used by flask-security
var SECURITY_PASSWORD_SALT = 'Many random bytes...';

var password_hmac = get_hmac(SECURITY_PASSWORD_SALT, password);
var h = new_hash(password_hmac, 20000);
console.log('HASH ' + h);
console.log('VERIFY ' + verify_hash(password_hmac, h));
        

遗憾的是 none 提到的解决方案对我有用 (passlib 1.7.4)。我最终编写了自己的小节点模块。如果有人仍然坚持这一点,请随时使用 node-passlib

示例:

import nodePasslib from 'node-passlib';

// python-passlib PBKDF2-SHA512 hash
const pbkdf2Hash = '$pbkdf2-sha512000$JKSU8r63VgrBmHMO4RwjRA$bUL/owmBl8slaj.fjONmdRijzOs4Lo6EwbKtoA6EPX1hs1BCdg3JRjfkR3WX5/mZ4cIhtJhFVFxrLlq1lHfpQw';

nodePasslib.verify('yourpassword', pbkdf2Hash);
// true

nodePasslib.verify('wrongpassword', pbkdf2Hash);
// false

默认 keylen 设置为 64。如果您使用 SHA256 编码哈希,则需要指定不同的 keylen:

nodePasslib.verify('yourpassword', pbkdf2Hash, 32);

希望对您有所帮助。