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

Cheap single thread password hashing algorithm

关于密码散列的另一个问题。

嗨,

我正在为一个运行单线程的网站开发一个 NodeJS 后端服务器实时传输模块和身份验证,这有很多原因。但是,由于 bcrypt/scrypt 等函数非常 CPU 密集,这些可能会导致线程阻塞并导致 return 糟糕的最终用户体验。

多年来我一直使用这样的函数来生成哈希和比较。

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

我最初不对密码加盐的原因是散列和加盐存储在同一个 table 中。因此,攻击者可能会使用暴力,一旦包含盐的字符串出现,就知道攻击成功了。

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

像 scrypt、bcrypt 和 PBKDF2 这样的密码扩展器旨在 CPU 密集。这就是它们存在的原因。如果给定的配置对于您的用例来说太昂贵,它们都可以调整以更改工作要求。

仅散列 10 次不如应用迭代次数为 10 的适当密码延伸器安全。如果是,则没有人会费心开发密码延伸器。

如果可能,我强烈建议您将拉伸组件移动到客户端,并将输出发送到服务器。这减轻了服务器的负担,但更重要的是完全避免了将原始密码发送到服务器。然后服务器可以以非常低的成本应用单个散列迭代。在大多数浏览器上,数千次 PBKDF2 迭代可以在不到 100 毫秒的时间内轻松完成,使用本机代码可以轻松执行 10k 次迭代。服务器上的 100 毫秒可能是一个大问题。客户端100ms一般可以隐藏一次

如果客户端保存密码,它可以保存密码拉伸器的输出。同样,这提高了安全性,因为原始密码永远不会存储在任何地方(甚至加密),并通过避免 re-stretching.

提高性能

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