廉价的单线程密码哈希算法



还有一个关于密码哈希的问题。

你好,

我正在为一个运行单线程的网站开发NodeJS后端服务器实时传输模块和身份验证,这有很多原因。然而,由于像bcrypt/scrypt这样的函数占用大量CPU,这些函数可能会导致线程阻塞,并反过来带来糟糕的最终用户体验。

多年来,我一直使用这样的函数来生成散列和比较。

const crypt = require("crypto");
let p = "some_random_password";
let s = salt();
let h = hash(p,s);
console.log("Hash               :", h);
console.log("Salt               :", s);
console.log("Password matched   :", comparepass(p,h,s));
// Generate hash.
function hash(password,salt){
    let r = crypt.createHash("sha256").update(password).digest("hex");
    let s = crypt.createHash("sha256").update(salt).digest("hex");
    for(i=0;i<10;i++)
        r = crypt.createHash("sha256").update(r+s).digest("hex");
    return r;
}
// Compare hash with a given password + salt.
function comparepass(password,hash,salt) {
    let c = crypt.createHash("sha256").update(password).digest("hex");
    let s = crypt.createHash("sha256").update(salt).digest("hex");
    for(i=0;i<10;i++)
        c = crypt.createHash("sha256").update(c+s).digest("hex");
    return c == hash;
}
// Generate random string 42 characters long obviously.
function salt(length = 42) {
    var r = '';
    var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    var cLen = characters.length;
    for (var i = 0; i < length; i++)
        r += characters.charAt(Math.floor(Math.random()* cLen));
    return crypt.createHash("sha256").update(r).digest("hex");
}

我最初之所以不加盐处理密码,是因为哈希和盐存储在同一个表中。因此,攻击者可能会使用暴力,一旦包含salt的字符串出现,就知道攻击成功了。

但这安全吗?还是我这样做忽略了一个明显的缺陷?

像scrypt、bcrypt和PBKDF2这样的密码拉伸器是CPU密集型的。这就是它们存在的原因。如果给定的配置对于您的用例来说过于昂贵,那么它们都是可调的,可以更改工作需求。

仅仅哈希10次不如应用迭代次数为10的正确密码拉伸器安全。如果是这样的话,没有人会去开发密码拉伸器。

在可能的情况下,我强烈建议您将拉伸组件移动到客户端,并将输出发送到服务器。这减轻了服务器的负载,但更重要的是,避免了将原始密码发送到服务器。然后,服务器可以以非常低的成本应用单个哈希迭代。在大多数浏览器上,PBKDF2的数千次迭代可以在100ms以下轻松完成,使用本机代码可以轻松执行10k次迭代。服务器上的100ms可能是一件大事。客户端上的100ms通常可以隐藏一次。

如果客户端保存密码,则可以保存密码拉伸器的输出。同样,这提高了安全性,因为原始密码从不存储在任何地方(甚至加密(,并通过避免重新拉伸来提高性能。

但至少,我会用标准的密码拉伸器来替换您的临时解决方案,并根据您的性能要求调整其迭代次数。

相关内容

  • 没有找到相关文章

最新更新