如何正确比较加密的密码字符串?

How can I compare encrypted password strings correctly?

我有一个带有用户名和密码输入框的基本 html 页面。连同登录按钮。我正在使用 cryptojs 来尝试比较加密字符串。

我认为我的问题是因为我随机生成了我的密钥和 iv。你们对我可以改变的地方有什么建议吗?

app.post('/authenticate',function(req,res){
conn.open(connString, function(err){
if(err) return console.log(err);

var loginID = req.body.LoginID,
    passWord = req.body.PassWord;

//-------------------------Security---------------------------
    // create random Word Arrays for key and Salt
    var key = CryptoJS.lib.WordArray.random(16);
    var iv  = CryptoJS.lib.WordArray.random(16);

    // Encrypt Password using key and Salt. Changes every time but will always decrypt to same password.
    var encrypted = CryptoJS.AES.encrypt(passWord, key, { iv: iv }).toString();
    var decrypted = CryptoJS.AES.decrypt(encrypted, key, { iv: iv }).toString();

    console.log(decrypted);

//-------------------------END Security------------------------    

conn.query("SELECT PassWord from pub.User WHERE LoginID ='" + loginID  + "'",function(err,data){

    if(err) return console.log(err);   
    res.json(data);

    setValue(data);

    function setValue(value) {
    someVar = value;
    }
        for(key in someVar) {
            if(someVar.hasOwnProperty(key)) {
                var value = someVar[key];
                console.log(value.PassWord);
                console.log(encrypted);

                    if(value.PassWord == encrypted)
                    {
                        console.log("pass");
                    }
                    else
                    {
                        console.log("Fail");
                    }
            }
        }

        conn.close(function(){
        console.log('Login Complete');
        }); 
    }); // conn.query

}); //END conn.open(connString, function(err){
}); // END app.post('/authenticate'

谢谢,

你为什么选择Cryptojs?,我认为有更好的选择,比如Bcrypt(这是我常用的),他们公开了一个函数来直接将字符串与哈希值进行比较,仅此而已。

试一试:https://www.npmjs.com/package/bcrypt

你是对的。通过使用随机生成的 key/iv,您的 encrypted 密码字符串将始终不同,即使它 decrypt 为相同的值。因此,您将无法按照自己的方式比较加密字符串。

我认为您应该问自己的第一个问题是,一旦密码存储在数据库中,您真的需要能够对其进行解密吗?如果没有,您最好使用简单的散列。您可以为此使用 Node 的内置 Crypto 程序包——这就是我通常用来存储散列密码的方法。我将一些实用方法放入 Utils 包中,例如:

const crypto = require('crypto');

/**
 * hashPassword creates a password hash from the supplied password and
 * salt values.
 *
 * @param {string} password
 * @param {string} salt
 * @returns {string}
 */
function hashPassword(password, salt) {
    let seed = sha1Base64(password, salt);
    return sha256Hex(seed);
}

/**
 * sha1Base64 returns a signature using the supplied string and key.
 *
 * @param {string} str
 * @param {string} key
 * @returns {string}
 */
function sha1Base64(str, key) {
    return crypto.createHmac('sha1', key)
        .update(new Buffer(str, 'utf8'))
        .digest('base64');
}

/**
 * sha256Hex returns a string hash of the supplied data.
 *
 * @param {string|number|object} data
 * @returns {string}
 */
function sha256Hex(data) {
    return crypto.createHash('sha256')
        .update(data)
        .digest('hex');
}

hashPassword 函数将创建一个 64 个十六进制字符串。您大致了解了对密码进行哈希处理以将其存储在数据库中,然后在提交登录表单时对用户密码进行哈希处理。同样的事情在这里,只是这不需要任何外部包。

但是,您每次仍然需要使用相同的盐。您可以通过环境变量分配一个系统范围的盐(更安全),在每个用户或每个帐户的基础上将一个保存在数据库中(不太安全),或者将一个放在配置文件中的某个地方(可能是一个坏的主意)。这实际上取决于您要保护的内容以及您必须有多狂热——您是为个人博客创建登录区域还是为大通银行创建面向消费者的网站?只要确保您的盐足够长且随机即可。您可以使用函数生成任意长度的随机字符串:

/**
 * randomString returns a random alphanumeric string of the specified length.
 *
 * @param {number} [length]
 * @param {boolean} [special]
 * @returns {string}
 */
function randomString(length = 10, special = false) {
    let chars = 'abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    if (special) chars += '-_%!@#$^&*';
    let cLength = chars.length,
        sRandom = '';
    for (let i = 0; i < length; i++) {
        sRandom += chars[Math.floor(Math.random() * cLength)];
    }

    return sRandom;
}

对于我做过的大多数事情,使用像这样的散列就足够了。

希望对您有所帮助!