是否值得在客户端对密码进行哈希处理

Is it worth hashing passwords on the client side

提问人:Zakaria 提问时间:9/15/2010 最后编辑:dimo414Zakaria 更新时间:6/4/2022 访问量:120253

问:

当我想安装登录系统时,我总是将给定密码的 MD5 与其在服务器端用户表中的值进行比较。

但是,我的一个朋友告诉我,“清除”密码可能会被网络软件嗅探。

所以我的问题是:在客户端对密码进行哈希处理是个好主意吗?它比在服务器端散列它更好吗?

密码客户端 哈希

评论

4赞 Cyclone 9/25/2012
我正在考虑在客户端对密码进行哈希处理,但只是为了让我放心,客户端的密码永远不会以明文形式存在于服务器端,这意味着他们可以更轻松地知道我不知道他们的实际密码,或者如果泄露,他们不能轻易放弃它。我疯了吗?
2赞 sffc 11/2/2013
只是为了完整起见,因为我们谈论的是安全性,并且 OP 中提到了 MD5:加密密码时应始终使用盐。使用普通的、无盐的 MD5 比在数据库中存储纯文本密码略好。
4赞 Teejay 2/2/2016
@Cyclone 仅在客户端进行哈希处理绝对是一个坏主意,因为如果攻击者以某种方式知道哈希值,他可以使用它来登录,就好像他知道密码一样,绕过客户端哈希代码。
8赞 MSalters 4/22/2016
@Teejay:这就是为什么你不以明文形式发送哈希值的原因。服务器向客户端发送一个随机的盐,你附加密码哈希,并再次对整个事情进行哈希,然后将其发送回服务器,服务器执行相同的计算。重放攻击失败,因为盐会不同
3赞 efr4k 5/31/2016
这个问题不应该在 security.stackexchange.com 结束吗?

答:

158赞 Dirk 9/15/2010 #1

基本上,你的朋友是对的。但是,简单地在客户端对密码进行哈希处理,只比将其作为纯文本提交给服务器要好。可以监听您的纯文本密码的人当然也能够监听哈希密码,并使用这些捕获的哈希来对您的服务器进行身份验证。

就此而言,更安全的身份验证协议通常会跳过许多障碍,以确保这种重放攻击无法奏效,通常允许客户端选择一堆随机位,这些随机位与密码一起散列,并以明文形式提交给服务器。

在服务器上:

  • 生成几位随机数
  • 将这些位(以明文形式)发送到客户端

在客户端上:

  • 生成一些随机位
  • 连接密码、服务器的随机位和客户端的随机位
  • 生成上述的哈希值
  • 向服务器提交随机位(明文)和哈希值

由于服务器知道自己的随机信息以及客户端的随机位(它以明文形式获取它们),因此它可以执行基本相同的转换。该协议确保,在此对话中监听的任何人都不能稍后使用信息使用记录的信息进行错误身份验证(除非使用了非常弱的算法......),只要双方每次产生不同的“噪声位”,就会执行握手。

编辑所有这些都容易出错且乏味,而且有些难以正确(阅读:安全)。如果可能的话,可以考虑使用已经由知识渊博的人编写的身份验证协议实现(与我不同!以上只是我前段时间读过的一本书的记忆。你通常真的不想自己写这个。

评论

7赞 Zakaria 9/15/2010
他如何在登录系统中使用哈希密码进行身份验证,因为它将再次被哈希处理?
4赞 Dirk 9/15/2010
如果您将密码以哈希形式存储在服务器上(您应该这样做),那么客户端将不得不对密码进行两次哈希处理:首先是单独的密码,因此它与服务器的世界观相匹配,然后如上所述,为了协议。
4赞 Pacerier 7/18/2012
@Dirk,由于攻击者可以双向嗅探,因此“随机位”将被嗅探。然后,攻击者可以离线分析(读作:暴力破解)请求和响应对,以找到原始密码。
8赞 AaronLS 4/1/2014
但是,为了提交密码或密码的哈希值,通常使用像 Diffie-Hellman 这样的非对称过程来建立加密密钥,该密钥允许双方交换信息,而无需窥探方解密。这正是SSL的作用,也是大多数网站在HTTPS / SSL上拥有登录页面的原因。SSL 已经可以防止重放攻击。我建议利用SSL而不是构建自己的协议。虽然我确实同意对密码客户端进行加盐+哈希处理,但通过已建立的SSL连接发送这些。
25赞 aidan 8/13/2015
需要明确的是:如果您不使用 HTTPS,那么在客户端上运行什么 javascript 并不重要;MITM 将能够在页面到达客户端之前修改页面的内容。HTTPS 是唯一的解决方案。
25赞 dimo414 9/15/2010 #2

你可能不用担心这一点 - 正如 Dirk 所提到的,即使你对密码进行哈希处理,恶意用户也可能在网络上并看到哈希被发送,并且可能只是自己发送相同的哈希。

稍微好一点,因为它可以防止恶意用户知道密码是什么,但由于他们仍然可以登录(或可能重建原始密码),因此这并没有那么有用。

一般来说,如果您担心用户密码和数据的安全性(您应该这样做!),则需要使用安全的SSL服务器。如果出于某种原因,这不是您关心的问题,您最好不要为散列而烦恼;这只是通过默默无闻获得安全


编辑 2014 年 8 月:谷歌越来越强烈地推动网站在任何地方都切换到HTTPS,因为保护通信本身是防止网络嗅探攻击的唯一方法。试图混淆传输的数据只会阻碍而不是阻止专门的攻击者,并可能给开发人员带来危险的虚假安全感。

评论

3赞 f1lt3r 9/6/2018
我认为,如果您专注于获取用户密码和访问单个服务,那么您认为客户端哈希只是“稍微好一点”是正确的。如果你考虑集体效应,我会说它“好得多”;因为它可以防止构建用于在多个服务之间暴力破解加盐哈希的密码的大型查找数据库。国际 海事 组织。查看 AWS Cognito 在客户端中的操作以供参考。x
1赞 Kfir Eichenblat 1/17/2022
我来晚了,但基于@f1lt3r的回答,我想阐明一个事实,即用户经常对多个服务使用相同的密码,或者至少有一个明确的模式。就我个人而言,我过去选择在客户端对密码进行哈希处理,只是为了向我的用户保证,我的服务器/我自己不知道他们的密码是什么
3赞 SmAxlL 6/30/2011 #3

这取决于,您可以使用客户端哈希,但服务器端盐是不可能的。

查看链接 客户端密码加密

1赞 ScottGal 6/9/2013 #4

我最近在这方面做了很多工作,IRL 客户端哈希/对称加密有两个问题,真正扼杀了这个想法: 1.你必须以某种方式将盐带回服务器......要在客户端加密它,您需要一个密码......这违背了目的。 2. 你暴露了你的哈希实现(这不是一个大问题,因为大多数网站使用 3 或 4 个哈希算法之一),这使得攻击更容易(因为只需要尝试一个而不是 n)。

我最终去的是使用 OpenPGP.js 或类似工具在客户端上进行非对称加密...... 这依赖于客户端上导入的或客户端生成的密钥以及发送其公钥的服务器。只有客户端的公钥才能发送回服务器。 这可以防止 MIM 攻击,并且与设备一样安全(我目前默认将客户端的私钥存储在 localStore 中,这是一个演示)。

这样做的主要优点是,我永远不必将用户数据存储在服务器/数据存储上未加密的内存中(并且我服务器的私钥在物理上是独立的)

其基础是为人们提供一种在HTTPS受到限制的情况下进行安全通信的方法(例如,伊朗/朝鲜atc...),也只是一个有趣的实验。

我远不是第一个想到这一点的人,http://www.mailvelope.com/ 使用它

1赞 GlynRob 10/21/2013 #5

如果有人可以在您的连接上看到数据传入和传出,则身份验证不会保存您。 相反,我会为超级秘密的东西做以下事情。

在发送到服务器之前在客户端预先哈希的密码。 (服务器存储从浏览器发送的该哈希值的另一个哈希值和加盐值)。

因此,中间人攻击可以允许他们发送与他们相同的哈希值来登录,但用户密码不会被知道。这将阻止他们尝试使用相同凭据的其他服务在其他地方登录。

用户数据在浏览器端也是加密的。

啊,所以中间人攻击会得到加密数据,但如果没有用于登录的实际密码,就无法解密它。(用户登录时存储在浏览器的 DOM 中的密码)。 因此,真正的用户会看到解密的内容,但中间人不能。 这也意味着任何 NSA 或其他机构都无法要求您/公司/托管服务提供商解密这些数据,因为他们也不可能这样做。

这两种方法的一些小例子在我的博客上 http://glynrob.com/javascript/client-side-hashing-and-encryption/

38赞 Adam Burley 1/17/2014 #6

实际上,我不同意在这种情况下客户端哈希更安全。我认为它不太安全。

在数据库中存储密码的哈希值而不是真实密码(甚至是加密密码)的全部意义在于,从哈希值中获取原始密码在数学上是不可能的(尽管理论上可以获得冲突哈希输入,其难度取决于哈希算法的安全强度)。这里可能的攻击媒介是,如果潜在的攻击者以某种方式破坏了您的密码存储数据库,他/她仍然无法获取您用户的原始密码。

如果您的身份验证机制发送密码的哈希值,那么在“安全漏洞”场景中(即攻击者以某种方式获得了您的密码数据库的副本),攻击者不需要知道真实密码 - 他们只需发送他们从漏洞中获得的哈希密码,嘿,他们有权访问该用户的帐户。这完全违背了首先存储散列密码的意义!

真正安全的方法是向客户端发送一次性公钥,让他们加密密码,然后在服务器端解密并重新散列。

顺便说一句,这种问题可能会在Security StackExchange上得到更多专家的回答。

评论

6赞 James Wright 7/9/2015
完全同意这一点。对于客户端的工作方法,还必须将盐发送给客户端,这将使加盐的整个目的无效!
0赞 MSalters 6/14/2017
@JamesWright:重点是每次使用都会发送一个随机盐,以防止非法重复使用登录消息。(重放攻击的一种形式)。每次都送同样的盐确实毫无意义。
3赞 Daniel Methner 2/28/2020
显然,如果你在客户端对它进行哈希处理,你将不得不在服务器端再次对它进行哈希处理,以防止你提出的观点。正如其他人所指出的那样,为了隐私,你已经需要客户端加密了。
2赞 user1034912 6/22/2020
正在发送一次性公钥...这不就是SSL的作用吗?
3赞 Adam Burley 7/5/2020
@user1034912是的,但我认为 OP 不仅想依靠 SSL 来确保安全。某些网络(尤其是企业网络)在某些负载平衡器处终止了 SSL,这意味着明文密码对服务应用程序以外的网络组件可见。通常,我们使用客户端加密来避免这种情况,并确保客户端和服务应用程序之间的 e2e 加密。
80赞 FriendlyCryptoNeighbor 5/10/2014 #7

首先,这不会提高应用程序的安全性(假设它是一个 Web 应用程序)。

使用 SSL(或者实际上是 TLS,通常称为 SSL)。使用 Certbot 进行设置是免费且简单的。

原因很简单。TLS 解决了一个在加密学中非常大的问题(当与证书颁发机构的证书一起使用时,而不是自签名):我如何知道我正在与之交谈的服务器是我认为我正在与之交谈的服务器?TLS证书是一种说法:“我,证书颁发机构,受您的浏览器信任,证明[url]的网站具有此公钥,并具有相应的私钥,只有服务器知道(私钥),看我在整个文档上签名了我的签名,如果有人更改了它,您可以看到”。

没有TLS,任何加密都变得毫无意义,因为如果我在咖啡店里坐在你旁边,我可以让你的笔记本电脑/智能手机认为我是服务器和MiTM(中间人)你。使用 TLS,您的笔记本电脑/智能手机会尖叫“不受信任的连接”,因为我没有与您的站点匹配的证书颁发机构签名证书。(加密与身份验证)。

免责声明:用户倾向于直接点击这些警告:“不受信任的连接?什么?我只想要我的小猫照片!添加例外 点击确认 点击YAY!小猫!

但是,如果您真的不想使用证书,请仍然实现客户端 Javascript 哈希(并使用斯坦福库 (SJCL),切勿自己实现加密)。

为什么?密码重用!我可以在没有 HTTPS 的情况下轻松窃取您的会话 cookie(这允许我向您的服务器假装我是您)(参见 Firesheep)。但是,如果您将 Javascript 添加到登录页面,在发送之前,它会对您的密码进行哈希处理(使用 SHA256,或者更好的是,使用 SHA256,向他们发送您生成的公钥,然后用它加密哈希密码,您不能使用盐),然后将哈希/加密密码发送到服务器。用盐重新散列服务器上的哈希值,并将其与数据库中存储的内容进行比较(像这样存储密码:

(SHA256(SHA256(password)+salt))

(将盐也保存为数据库中的纯文本))。并像这样发送您的密码:

RSA_With_Public_Key(SHA256(password))

并像这样检查您的密码:

if SHA256(RSA_With_Private_Key(SHA256(sent_password))+salt_for_username) == stored_pass: login = ok

因为,如果有人嗅探您的客户端,他们将能够以您的客户端身份登录(会话劫持),但他们永远不会看到明文密码(除非他们更改了您的 Javascript。但是,星巴克黑客可能不知道如何或对此不感兴趣。因此,他们将访问您的 Web 应用程序,但不能访问他们的电子邮件/Facebook/等(您的用户可能会使用相同的密码)。(电子邮件地址要么是他们的登录名,要么是他们的个人资料/设置中的 Web 应用)。

评论

1赞 Neyt 11/23/2018
我认为这是一个很好的答案,但它可能需要更多关于如何正确加盐的信息,并且在将密码存储在服务器上(如 bcrypt)之前最好使用“慢速”哈希函数。
1赞 T. Salim 8/19/2020
是的,与其直接使用 SHA256,不如使用 bcrypt 甚至更安全的 argon2 实现。
0赞 Mirek 8/24/2023
应用程序通常在 TLS 终止服务器中的反向代理后面运行。在某些公司设置中,即使在 HTTPS 上发送任何明文密钥仍然不行,因为在 TLS 终止后,机密可能会被记录在某种 HTTP 日志中。
0赞 cneeds 11/4/2017 #8

考虑一下:-

客户端向服务器发送请求“我有要验证的密码”。

服务器向客户端发送一次性随机字符串。雷亚尔

客户端将用户的密码嵌入到此字符串中(基于您希望应用的任何(变量)规则)。

客户端将字符串发送到服务器,如果密码正常,则服务器将用户登录。

如果服务器收到另一个使用 R$ 的登录请求,用户将被注销,帐户将被冻结,等待调查。

显然,将采取所有其他(正常)安全预防措施。

6赞 Eric Twilegar 5/4/2018 #9

最近,GitHub和Twitter都宣布将密码存储在内部日志中。我在错误报告和其他日志中无意中发生了这种情况,这些日志进入了 splunk 等。对于Twitter来说,如果特朗普的密码在日志中,那么管理员“看到”它可能是一件大事,对于其他网站来说,可能没什么大不了的,因为管理员对它没有太大用处。无论如何,作为管理员,我们不喜欢看到密码,不是吗。

因此,问题在于是否应该在客户端进行哈希以确保安全,但是我们如何在密码最终被服务器端进行哈希和比较之前保护密码,以免以某种方式被记录下来。

加密并不是一个坏主意,因为开发人员至少必须跳过一些障碍,如果您发现密码进入了日志,您可以更改加密密钥,销毁原始密钥,然后数据变得毫无用处。更好的是每晚轮换钥匙,它可以大大减少窗户。

您还可以在用户记录中对哈希进行哈希处理。泄露的密码将是散列的纯文本密码。服务器将存储哈希的哈希版本。当然,哈希会成为密码,但除非你有照相记忆,否则你不会记住 60 个字符的密码。Salt 替换为用户名。如果你能在登录过程中收集一些关于用户的信息(同时不暴露用户记录的存在),你也可以用它来创建一个更强大的哈希值,不能在站点之间共享。中间的任何人都无法在站点之间剪切和粘贴捕获的哈希值。

结合一个没有被提交回服务器的cookie,你可能会遇到一些事情。在第一次请求时,使用密钥向客户端提交一个 cookie,然后确保该 cookie 不会返回登录服务,因此它被记录的可能性很小。将密钥存储在会话存储中,然后在登录发生后或会话过期后立即将其删除...这需要你们 JWT 家伙的状态,但也许只是使用 nosql 服务。

因此,在这条路上,管理员会在 splunk 或错误报告工具中遇到这些散列和加密的密码之一。这对他们来说应该是无用的,因为他们再也找不到加密密钥了,即使他们找到了,他们也必须暴力破解哈希。此外,最终用户没有发送任何明文,所以中间的任何人至少都有更困难的时间,你不能只是跳到另一个网站并登录。

评论

2赞 Dan 6/26/2018
正是我所关心的。如果服务器由于实施不当或恶意意图而意外记录,那么如果用户重复使用密码,则用户的其他帐户可能会受到威胁。
1赞 stewSquared 10/23/2021
如果使用从密码客户端生成的哈希进行身份验证,则也不希望在日志中看到该哈希值。
7赞 Raphael 8/30/2018 #10

请注意,保护密码免受第三方侵害并不是全部。

一旦涉及隐私(现在什么时候不涉及隐私?不想知道密码。你不能滥用或泄露你没有的东西,所以如果你从来没有看到他们的明文密码,你和你的客户都可以睡得更好。

因此,对客户端进行哈希/加密是有意义的。

评论

2赞 f1lt3r 9/6/2018
同意!很多人都忽略了这一点。如果我下载您的加盐哈希密码密码数据库,并且我可以使用哈希查找数据库破解一个,那么我很可能可以破解所有密码。一旦我有了 50k 个原始密码,我现在就拥有了仅在服务器上加密的服务上的用户的密钥。如果每个服务在离开客户端之前对密码进行唯一哈希处理,则跨服务密码的大型数据库会变得非常小。检查您的 AWS 登录流量,看看他们做了什么。这是概率,我亲爱的华生。xn
1赞 S To 4/18/2021
我完全同意,出于多种原因,客户端的哈希更合乎逻辑。如果黑客想知道实际的哈希密码,他们将很难弄清楚。通过“安全”连接发送明文密码是完全愚蠢和愚蠢的。在黑客世界中没有“安全”连接这样的东西。在用户端对用户密码进行哈希处理可以带来新的安全算法,黑客很难通过跳闸服务器检测来看起来他们是实际用户。
2赞 n8isjack 4/5/2020 #11

这种客户端哈希的想法是为了保护用户,而不是您的网站。如前所述,纯文本或散列密码都可以平等地访问您的网站。您不会获得安全权益。

但是您的用户实际的纯文本密码应该只有他们自己知道。知道他们选择的密码是可以在其他站点和系统上对他们使用的信息。您是一个以客户为中心的网站,通过保护他们的密码选择不被您的服务器开发人员或第三方发现。

0赞 Daniel Rudy 3/5/2022 #12

我想出的方法是使用 SHA-256 进行多轮和随机盐......

salt = generate random salt
hash = sha256(password)
for (i = 0 to rounds)
    hash = sha256(hash + salt)

这将以只有用户知道密码是什么的方式对密码进行加密。salt 和 hash 都存储在数据库中。我还使用服务器生成的随机字符串在客户端实现了这一点。

server_data = {
    algorithm,
    rounds,
    salt,
    string
}

hash = hash(server_data.algorithm, password)
for (i = 0 to server_data.rounds)
    hash = hash(server_data.algorithm, hash + salt)
hash = hash(server_data.algorithm, hash + server_data.string)

在加密课上,我们被教导攻击者可以知道算法、盐、回合等......但在这种情况下,永远不会发送实际的哈希密码。服务器知道它发送的随机字符串是什么,因此在客户端上生成完整的哈希值,最后部分是使用随机字符串对结果进行哈希处理。这是发送到服务器的结果。结果实际上是一个一次性密码,在重放攻击中毫无用处。用户的密码永远不会以明文形式发送,如果攻击者获得了密码哈希,则对他们来说毫无用处,因为服务器不会以这种形式接受它。黑客可能能够计算出盐的彩虹表和轮数来尝试暴力破解密码,但是任何使用至少 8 个字符长的 ASCII 符号 32-126 的足够复杂的密码通常需要很长时间才能破解......这是针对每个密码的。我为之编写的站点的密码长度最多为 128 个字符。

至于会话劫持,可以通过每 30 秒左右重新生成会话 ID 来合理缓解这种风险。我还包含一个嵌入在页面本身的 HTML 中的令牌。在服务器端,一些客户端信息(如客户端的 IP 地址和用户代理字符串)也会被哈希处理并存储在用户的会话数据中。

因此,在每个请求中,服务器都会获取 cookie 会话 ID 和令牌。令牌将与会话数据中的内容进行比较。其他信息(如 IP 地址和用户代理)也会进行哈希处理,并与会话中的信息进行比较。如果全部匹配,那么这很有可能是真正的用户,而不是入侵者。

我可以看到这种情况发生的唯一方法是,如果攻击者拥有嵌入式令牌、相同的 IP 地址和相同的用户代理。这一切都与概率有关。此外,任何时候您都在处理加密货币,可能很难做对。所以正如其他人所说,不要自己动手。

-1赞 theking2 6/4/2022 #13

现在是 2022 年。随着黑客攻击变得越来越复杂,幸运的是也有浏览器标准。事实上的传输协议是加密的 (https),使用内容安全策略CORS 系统地进行跨站点脚本或重放攻击更加困难。

我相信您的时间将花在熟悉这些技术上。

我不是安全专家,把它交给更有能力的工程师。