https 安全性 - 密码应该在服务器端还是客户端进行散列?

信息安全 Web应用程序 密码 tls 验证 哈希
2021-08-15 23:59:11

我正在构建一个需要用户登录的 Web 应用程序。所有通信都通过https。我正在使用 bcrypt 来散列密码。

我面临着一个两难境地——我曾经认为在客户端(使用 JavaScript)制作密码哈希更安全,然后将其与数据库服务器端的哈希进行比较。但我不确定这比通过 https 发送纯文本密码然后在服务器端对其进行散列更好。

我的理由是,如果攻击者可以拦截 https 流量(= 读取明文密码),他还可以更改 JavaScript,以便它将明文密码与散列密码一起发送 - 他可以在其中拦截它。

反对散列客户端的原因只是易于使用。如果我在客户端散列,我需要使用两个单独的库进行散列。这不是一个无法克服的问题,而是一个令人讨厌的问题。

使用客户端散列是否有安全收益?为什么?

那我也应该使用挑战-响应吗?

更新:我最感兴趣的是 - 这些技术(客户端散列、请求-响应)是否在使用https的情况下增加了任何显着的安全增益?如果是这样,为什么?

4个回答

如果您在客户端进行散列,则散列后的密码将成为实际密码(散列算法只不过是将用户持有的助记符转换为实际密码的一种手段)。

这意味着您将在数据库中存储完整的“纯文本”密码(哈希),并且您将首先失去哈希的所有好处。

如果你决定走这条路,你不妨放弃任何散列,并简单地传输和存储用户的原始密码(顺便说一句,我不会特别推荐)。

仅当您不以某种方式信任服务器并且不想向其显示“实际”密码(人类用户记住的密码)时,客户端上的散列才有意义。为什么您不想将密码显示给该密码有任何用途的站点?因为您在其他地方重复使用了密码!现在这通常很糟糕,但是有一个相对安全的版本,它体现在无数浏览器扩展或书签中,例如这个那个(我不保证它们的质量)。这些是人类用户记住“主密码”的工具,从中生成特定于站点的密码,使用站点域名作为一种盐,以便两个不同的站点获得不同的密码。

虽然这种情况是有道理的,但使用服务器本身发送的 Javascript 来做这件事就没有意义。事实上,散列密码客户端的关键在于服务器可能具有敌意(例如被攻击者破坏),因此该服务器发送的 Javascript 代码至少是可疑的。您不想在某些恶意 Javascript 中输入您宝贵的密码...


客户端散列的另一种情况是慢散列由于根据定义,密码很弱,因此您希望阻止字典攻击您假设坏人获得了服务器数据库的副本,并将在他自己的机器上“尝试密码”(有关此问题的一些讨论,请参阅此博客文章)。为了减慢对手的速度,你使用了一个固有的慢散列过程(例如bcrypt),但这会使每个人的处理速度变慢,包括服务器。为了帮助服务器,您可能希望卸载客户端上的一些工作,因此至少在客户端浏览器中运行的一些 Javascript 代码中完成部分工作......

不幸的是,Javascript 在这种工作上非常慢(通常比体面的 C 代码慢 20 到 100 倍),并且客户端系统将无法为散列工作做出很大贡献。这个想法是合理的,但必须等待更好的技术(尽管它可以与Java客户端一起使用:对于一个体面的 JVM,优化的 Java 代码比优化的 C 代码慢大约 2 到 4 倍,用于哈希作业)。


总而言之,从服务器本身发送的 Javascript 代码中进行客户端密码散列并没有真正好的案例。只需通过 HTTPS 隧道“按原样”将密码发送到服务器(登录页面、表单目标 URL 以及任何受密码保护的页面,都应通过SSL 提供,否则您将面临比使用密码)。

正如所有其他答案所表明的那样,散列服务器端很重要,但我想补充一点,除了服务器端散列之外,散列客户端将是一个很好的安全功能。

客户端散列在以下场景中具有优势:

  1. 在服务器受到威胁时保护用户的密码。即,如果客户端没有受到威胁,但服务器是,如果客户端对密码进行哈希处理,服务器仍然可以访问一个系统,但是您已经保护了用户的密码,如果他们在其他地方使用该密码,这一点很重要。
  2. 当用户认为他们正在登录到一台服务器但他们实际上正在登录到另一台服务器时保护用户的密码(用户错误)。例如,如果我有两个银行账户,并且我不小心将我银行的一个密码输入了错误的银行网站,如果银行在客户端对密码进行哈希处理,那么该银行将不知道我另一家银行的密码。我认为对客户端进行哈希处理是一件“礼貌”的事情,这样他们的纯文本密码就不会通过网络传输。

大多数情况下,它表示对用户密码的尊重。用户正在共享一个可能不是您的软件独有的秘密,因此如果您尊重该秘密,您应该尽您所能保护它。

我发现您的所有担忧都是正确的,但我的建议是在服务器端进行。

用户总是有相当大的机会将他们的终端解锁,允许操作。并且; 如果您的散列逻辑是客户端的,那么您正在公开它。

另一种选择是在服务器端生成密码;那么您不会发送明文密码。但是您仍然需要将密码传达给用户。而且由于大多数用户仍然不使用加密电子邮件,我认为这不太安全。

我见过通过加密隧道将密码发送到手机的解决方案;但我怀疑安全性比 SSL 更好。也许有人可以证明/反驳这一点?