在将密码发送到服务器端之前,是否应该对其进行哈希处理?

我注意到,大多数站点通过 HTTPS 以纯文本的形式将密码发送到服务器。如果我将密码的哈希值发送给服务器,而不是发送给服务器,那么有什么好处吗?这样会更安全吗?

83377 次浏览

不,事实上这是一个弱点。如果攻击者能够从数据库中获得散列,那么他就可以使用它来进行身份验证,而不需要破解它。在任何情况下,用户或攻击者都不应该能够获得哈希密码。

散列密码的全部意义在于增加一个额外的安全层。如果攻击者能够使用 SQL 注入或不安全的备份从数据库中获得 hash 和 salt,那么他必须通过强制方式找到纯文本。开膛手约翰通常用于破解加盐的密码哈希。

不使用 https 违反了 OWASP Top 10: A9-传输层保护不足

编辑: 如果在实现中计算了 sha256(client_salt+plain_text_password),然后在服务器端 sha256(server_salt+client_hash)上计算了另一个散列,那么这不是一个严重的漏洞。但是,它仍然容易受到窃听和重放请求的影响。因此,这仍然是一个明显的违反 WASP A9。但是,这仍然利用消息摘要作为安全层。

我所见过的最接近客户端替代 https 的东西是 用 javascript 实现密钥交换。然而,这确实阻止了活动的 MITM 攻击,因此直到技术上都是对 OWASPA9的侵犯。代码的作者同意这并不能完全替代 HTTPS,但是它总比什么都没有好,也比客户端哈希系统好。

因为它是通过 HTTPS 发送的,所以发送密码时不需要散列(通过 HTTPS 发送密码不是明文)。此外,如果应用程序依赖 HTTPS 来保证内容的安全,那么在通过 HTTPS 发送之前对密码进行哈希是没有用的(也就是说,如果攻击者可以解密网上的数据,那么无论如何你都完蛋了)

实际上,散列密码并通过非加密通道发送密码的安全性更低。您将在客户端上公开您的哈希算法。黑客可以只是嗅出密码的杂凑,然后用它来黑进以后。

通过使用 HTTPS,您可以防止黑客从单个源获取密码,因为 HTTPS 使用两个通道,都是加密的。

如果连接到 https 服务器,服务器和浏览器之间的数据流应该被加密。数据在发送前和接收后仅为纯文本。维基百科文章

使用 HTTP 摘要-它甚至通过 HTTP 来保护密码(但最好的使用方式是通过 https 来进行 HTTP 摘要)

维基百科:

HTTP HTTP摘要认证是 web 服务器可以用来与 web 用户(使用 HTTP 协议)协商凭证的公认方法之一。摘要认证的目的是取代未经加密的 HTTP基本认证,让用户无须透过网络以明文形式发送密码,便可安全地确定身分。摘要身份验证基本上是 MD5加密哈希的应用程序,使用 nonce 值来防止密码分析。

链接: http://en.wikipedia.org/wiki/Digest_access_authentication

如果您希望看到“真实生活”的使用,可以查看 phpMyID-一个使用 http 摘要认证 http://siege.org/phpmyid.php的 php openid 提供程序

. . 或者你可以从 http://php.net/manual/en/features.http-auth.php的 php auth 样本开始

Http 摘要 rfc: http://www.faqs.org/rfcs/rfc2617

根据我的测试,所有现代浏览器都支持它..。

通过连接发送哈希完全违背了哈希的目的,因为攻击者可以简单地发送哈希并忘记密码。简而言之,使用清晰文本中的 hash 进行身份验证的系统是非常开放的,只需要进行网络嗅探就可以了。

如果您希望使用 HTTP 上的散列密码替换 HTTPS 上的明文密码,那么您就是在自找麻烦。HTTPS 在打开通信通道时生成一个随机的、共享的事务密钥。这很难破解,因为您几乎只能强制使用用于(相对)短期事务的共享密钥。而你的大麻可以只是闻闻,离线,在一个彩虹表或只是粗暴的强迫了很长一段时间。

但是,通过 HTTPS 发送的基本客户端密码混淆(而不是散列)确实有一些价值。如果我没有弄错的话,这种技术实际上被一些银行所使用。这种技术的目的不是保护密码不被监听。相反,它是为了阻止密码被愚蠢的间谍工具和浏览器插件使用,这些工具和插件只能获取它们看到的每个 HTTPS GET/POST 请求。我见过一个从恶意网站捕获的日志文件,它是从用户会话捕获的400MB 随机 GET/POST 事务。你可以想象,只使用 HTTPS 的网站会在日志中显示明文密码,但是有非常基本的模糊处理(ROT13)的网站也会显示不能立即使用的密码。

这是一个老问题,但我觉得有必要就这个重要问题提出我的意见。这里有太多的错误信息

OP 从未提到通过仅 HTTP 的 HTTPS 发送密码,但是出于某种原因,许多人似乎都在回应通过 HTTP 发送密码的问题。也就是说:

我认为密码不应该以纯文本形式保留(更不用说传输了)。这意味着不能保存在磁盘上,甚至不能保存在内存中。

在这里回应的人们似乎认为 HTTPS 是灵丹妙药,但事实并非如此。然而,它当然有很大的帮助,并且应该在任何经过身份验证的会话中使用。

真的没有必要知道什么是原始密码。所需要的只是一种可靠的方法,根据用户选择的原始文本生成(并可靠地重新生成)身份验证“密钥”。在一个理想的世界中,这个文本应该立即生成一个 “钥匙”,使用一个故意慢的哈希算法(如 bcrypt,以防止暴力)盐,然后不可逆地哈希它。该 salt 应该是所生成的用户凭证的唯一用户。 这个“密钥”将是您的系统使用的密码。这样,如果您的系统在将来受到损害,这些凭证将只对您自己的组织有用,而且在其他任何地方用户已经懒惰并使用相同的密码。

所以我们有钥匙。现在我们需要清除客户端设备上的任何密码痕迹。

接下来我们需要拿到你们系统的钥匙。你永远不应该传输一个密钥或密码“在明确的”。即使是通过 HTTPS 也不行。HTTPS 并非无懈可击。事实上,许多组织可以成为一个受信任的 MITM ——不是从攻击的角度,而是对流量执行检查,以实现他们自己的安全策略。这会削弱 HTTPS,而且它不是唯一发生的方式(例如重定向到 HTTPMITM 攻击)。永远不要认为它是安全的。

为了解决这个问题,我们 加密的关键与一次关闭现在。
对于向系统提交的每个密钥,这个立即性是唯一的——即使是在同一个会话中,如果需要多次发送相同的凭据,也是如此。您可以逆转所说的 nonce (解密) ,一旦它到达您自己的系统中,就可以恢复身份验证密钥,并对请求进行身份验证。

在这一点上,我会 不可逆转散列它最后一次之前,它是永久存储在您自己的系统。这样,您可以为了 SSO 等目的与合作伙伴组织分享凭证的盐,同时能够证明您自己的组织不能模仿用户。这种方法最好的地方在于,您永远不会在没有用户授权的情况下共享用户生成的任何内容。

做更多的研究,因为它比我透露的还要多,但是如果你想为你的用户提供真正的安全,我认为这个方法是目前最完整的响应。

译者:

使用 HTTPS。 安全地散列密码,不可逆地,每个密码使用唯一的 salt。在客户端上这样做-不要传输他们的实际密码。将用户原始密码传输到您的服务器绝不是“ OK”或“ Fine”。清除原始密码的任何痕迹。 不管 HTTP/HTTPS 如何使用 马上,它在许多层面上都更加安全。(回答 OP)。

明文显示的密码永远不会离开客户端(即使在使用 HTTPS 时也不会)。在离开客户端之前,应该对它进行不可逆的散列,因为服务器不需要知道实际的密码。

然后哈希传输解决了懒惰用户在多个位置使用相同密码的安全问题(我知道我是这样做的)。然而,这并不能保护您的应用程序作为一个黑客获得了访问数据库的权限(或以任何其他方式能够获得哈希) ,因为黑客可以只传输哈希,并让服务器接受它。

要解决这个问题,你当然可以只散列服务器收到的散列,并调用它一天。

我在创建一个基于套接字的 Web 应用程序时,解决这个问题的方法是,在连接到客户端时,服务器生成一个 salt (在散列之前添加的随机字符串)并将其存储在套接字变量中,然后将这个散列传输给客户端。客户端获取用户密码,对其进行哈希处理,添加服务器上的 salt 并对整个过程进行哈希处理,然后再将其传输到服务器。然后它被发送到服务器,服务器将这个 hash 与 hash (DB + salt 中的 hash)进行比较。据我所知,这是一个很好的方法,但公平地说,我没有读过很多关于这个主题,如果我错了,我希望得到纠正:)

SSL/TLS 不是正在取代 nonce 吗? 我不认为这有什么附加价值,因为 SSL/TLS 还能防御重播攻击。

裁判。 Https://en.wikipedia.org/wiki/cryptographic_nonce

免责声明: 我绝对不是一个安全专家——我在 希望上发帖称,其他人会批评我的立场过于谨慎或可以改进,我会从中学习。话虽如此,我只是想强调,当哈希离开你的客户端并不意味着你不必在后端哈希之前把它放入数据库。

两样都做

两者兼顾,因为:

  1. 搭乘过程中的哈希有助于掩盖传输的漏洞,如果 SSL 连接被破坏,他们仍然无法看到原始密码。就能否模拟授权用户而言,这并不重要,但它将保护您的用户不会让他们的密码与他们的电子邮件联系在一起被读取。大多数人不遵循最佳实践,并使用相同的密码为许多他们的帐户,所以这可能是一个严重的漏洞,您的访问者。

  2. 如果有人能够以某种方式从数据库中读取密码(这确实会发生,想想 SQL 注入) ,他们仍然无法通过我的 API 执行模拟用户的特权操作。这是因为散列不对称; 即使他们知道存储在数据库中的散列,他们也不会知道用于创建它的原始密钥,而这正是您的 auth 中间件用来进行身份验证的。这也是为什么您应该总是在散列存储器中加盐。

当然,如果他们可以自由地从您的数据库中读取他们想要读取的内容,那么他们可能会造成很多其他的损害。

我只是想在这里强调一下,如果你决定在离开你的客户端之前对密钥进行哈希,那是不够的——后端哈希更加重要,这就是为什么: 如果有人从你的客户端拦截流量,那么他们会看到 password字段的内容。无论这是散列还是纯文本,这都无关紧要——他们可以逐字复制它来模拟授权客户机。(除非您按照@user3299591所列出的步骤进行操作,我建议您这样做)。另一方面,对 DB 列进行哈希处理是必要的,并且一点也不难实现。

是否有优势,是否更安全(或更不安全)实际上取决于实现。可以说这样做有一些好处,但是如果实现得不好,您肯定可以创建一个比传递明文密码更不安全的解决方案。

可以从两种类型的攻击的角度来看待这个问题——一种是访问网络流量,另一种是访问数据库。

如果攻击者可以拦截网络流量的明文版本,那么查看密码的散列比查看明文密码更安全。虽然攻击者仍然可以使用该哈希登录到您的服务器,但是它需要对该哈希进行强力破解(有时是预先计算的) ,以确定可能在其他系统上有用的密码。人们应该在不同的系统上使用不同的密码,但通常不这样做。

如果攻击者获得了对数据库的访问权限,可能是通过备份的副本,那么您需要确保仅凭这些信息无法登录。例如,如果您存储了一个带有登录名 hash(login_name+password)的 hash,并从客户端传递了同样的 hash 进行直接比较,那么攻击者可以随机挑选一个用户,发送从数据库读取的 hash,并在不知道密码的情况下以该用户的身份登录,从而增加了攻击的范围。在这种情况下,以明文形式发送密码会更加安全,因为攻击者需要知道明文才能登录,甚至需要拥有数据库的副本。这就是实现的关键所在。无论是发送明文密码还是该密码的客户端散列,都应该在服务器端散列该值,并将该散列与存储在用户记录中的散列进行比较。

要记住的概念:

  • 通过将某个范围唯一的值(通常是行唯一的值)混合到散列中,可以对散列进行“加盐”。它的目的是保证哈希值之间的唯一性,即使它们所代表的明文值是相同的,所以两个密码相同的用户仍然会有不同的哈希值。没必要把盐当成秘密。
  • 在进行身份验证时,无论从客户端传递什么值作为密码(即使已经进行了哈希处理) ,始终在服务器端进行哈希处理,并将其与存储在数据库中的预哈希值进行比较。这可能需要存储原始密码的双哈希版本。
  • 制作散列时,请考虑向散列中添加一个服务器/集群唯一的 salt 以及一个行唯一的 salt,以防止在查找表中匹配任何预计算的散列。

如果您希望实现与通过 https 传输时相同的可靠性,那么可以选择——一个选项——在 ajax 级别实现非对称加密通道。

如果我们不讨论注册(例如,密码的第一次传输总是受到保护) ,那么还有其他选择。

比如说,

  1. 服务器生成一个 随机字符串并生成一个 发送给用户。
  2. 用户根据其密码计算哈希,并使用此哈希作为 一个密钥用河豚加密这个随机字符串,例如 肯定是 JS 中的一个实现) ,并将其发送回给您。
  3. 您可以自己使用存储在服务器上的散列对此进行加密 河豚随机线。
  4. 比较一下。

攻击者必须使用随机源码和密文来攻击蓝鱼密钥,这项任务并不容易。