当源代码可用时,如何正确保护密码

How do I properly protect a password when the source code is available?

本文关键字:何正确 保护 密码 源代码      更新时间:2023-09-26

我正在构建一个web应用程序,该应用程序具有用户登录功能,我希望使其尽可能安全。当创建一个新用户时,我使用这个javascript函数来散列用户的信息,并最终将输出存储在mySQL数据库中:

function hash(user) {
    var part1 = mix(user.username, user.password);
    var part2 = mix(user.random_value + salt, user.password);
    return mix(part1, part2);
    function mix(part1, part2) {
        var hash = sha3_256(part1 + part2);
        var rehash = 10000;
        while(rehash--) {
            hash = sha3_256(hash + salt + rehash);
        }
        return hash;
    }
}

数据库包含用户名、随机值和hash函数的输出,而salt变量是在函数外部声明的随机字符串,永远不会更改。

根据我之前阅读的一些关于密码安全的帖子,这是一种相当安全的做事方式(尽管我很感激任何建议),但我担心的是:

如果我假设有人可以访问数据库,获得为某个用户存储的所有信息,并且他可以准确地看到哈希函数的操作方式(因为这些代码在浏览器和GitHub上都可见)。难道这个恶意的人就不能通过我的函数运行所有可能的8个字符(最小密码长度)的组合,看看密码的值能创建正确的哈希吗?

这肯定需要一些时间,但最终浏览所有可能性并因此访问该帐户并非不可能。

有什么方法可以防止这种攻击吗?

谢谢!

附带说明:我假设您在服务器端(nodejs?)执行此操作。您的实现是非标准的,并且您的术语是混合的(正常的术语将"salt"作为一个值,每次都会更改,而"pepper"是一个固定的服务器端字符串),但它确实有主要的概念。不过,我建议您使用标准的"密码哈希"算法,如scrypt或bcrypt。

关于你的问题"这个恶意的人不能通过我的函数运行所有可能的8个字符(最小密码长度)的组合,看看密码的值能创建正确的哈希吗?",答案是肯定的,但他需要很长时间才能做到这一点。这就是rehash变量的全部目的:降低用户的速度。请参阅:我们的密码哈希没有衣服了解更多细节。

您的威胁模型是"攻击者可以完全读取数据库内容",是吗?

解决方案是:

  1. 不要让这种事发生
  2. 将web服务器与数据库服务器分离,并使用Peppering

这涉及到将机密存储在web服务器上(从不存储在数据库服务器上)。首先使用此选项HMAC密码。然后将其散列存储。

如果数据库服务器遭到破坏,密码就无法轻易破解。即使攻击者可以进行SQL注入,但无法获得web服务器信息,这也会起作用

注:

  1. 不要将SHA{anything}用于密码哈希。仅使用bcrypt、scrypt或PBKDF2
  2. 不要使用自己的安全系统。使用现有框架
  3. 您的系统应该是安全的,即使它是开源的。看见https://en.wikipedia.org/wiki/Kerckhoffs%27s_principle
  4. 在你的网络上运行HIDS,在你的数据库服务器上运行NIDS,这样你就可以(希望)检测到漏洞并通知你的用户,这样他们就可以在其他网站上更改密码

如果您使用的是一个足够好的哈希算法,那也没关系。这是因为(强调矿

密码散列函数是一种数学算法,它将任意大小的数据映射到固定大小的比特串(散列函数),该比特串也被设计为单向函数,也就是说,不可行反转的函数。

加密哈希被设计为开源的,在明文密码存储中使用它们的目的是,如果数据被破坏,根密码是不可逆的。此上的Wiki

现在,如果你自己滚动了,或者试图阻止别人知道你在使用弱哈希算法,你就有麻烦了。

此外,您的盐/胡椒应该只存在于应用程序的实例化中。您当然可以将其加载到github,但我建议您在实际应用程序中更改该配置。

为了解决您对每个8个字符的密码都要运行的问题,这只是一个彩虹表问题。这就是为什么使用hunter2是一个错误的密码。你的盐/胡椒有助于缓解这种情况,但总的来说,猜测是不可行的。这很好地说明了头脑中的时机。

难道这个恶意的人就不能通过我的函数运行所有可能的8个字符(最小密码长度)的组合,看看密码的值能创建正确的哈希吗?

当然一个好的密码哈希的目标是减缓对手生成每个哈希所需的时间,尤其是因为在传统CPU上可能"慢"的东西在GPU(或许多;想想加密采矿场)或ASIC上可能很快。

这就是为什么设计了bcrypt、scrypt和Argon等密码哈希算法。(问题中显示的函数更类似于PBKDF2,它不是内存硬。)

因此,忽略仅依赖于代码外存储/管理的附加秘密的尝试(例如PKI私钥或"胡椒"),两个通用选项是:

  • 使对手的哈希函数变慢。

    请参阅上面提到的密码散列函数之一,它在GPU/ASIC上提供不同级别的硬度;

  • 帮助用户选择具有更多密钥熵的更长的密码短语。这有一个隐含的目标,即在常见的密码字典中找不到这样的"密码"。

    除了用户教育(例如清晰的消息传递)帮助是要求"密码"至少为12/14个字符,并且取消一些传统限制。像输入空间增加了,即使是快速暴力方法变得不那么吸引人。参见强制性链接


由于显示的原始自旋转函数与PBKDF2相比没有固有优势,而且缺点是不是一个经过仔细检查的算法,因此我建议使用具有适当盐/输出大小和迭代次数的PBKDF2

还可以通过一个KDF(包括一轮的PBKF2)来简单地支持一个"pepper",以提供实际的迭代哈希。