是否值得在客户端散列密码
Posted
技术标签:
【中文标题】是否值得在客户端散列密码【英文标题】:Is it worth hashing passwords on the client side 【发布时间】:2011-04-12 13:40:00 【问题描述】:当我想建立一个登录系统时,我总是将给定密码的 MD5 与其在服务器端用户表中的值进行比较。
但是,我的一个朋友告诉我,“明文”密码可能会被网络软件嗅探。
所以我的问题是:在客户端散列密码是个好主意吗?比在服务器端散列更好吗?
【问题讨论】:
我正在考虑在客户端对密码进行哈希处理,但只是为了让我可以放心,客户端的密码永远不会以明文形式存在于服务器端,这意味着知道我不这样做他们会感觉更轻松不知道他们的实际密码,或者如果被泄露也不能轻易放弃。我疯了吗? 为了完整起见,因为我们正在谈论安全性,并且 OP 中提到了 MD5:One should always use a salt when encrypting a password. 使用纯的、未加盐的 MD5 比在数据库中存储纯文本密码略好。跨度> @Cyclone 仅在客户端散列绝对是一个坏主意,因为如果攻击者以某种方式知道散列,他可以使用它来登录,就好像他知道密码一样,绕过客户端散列代码。 @Teejay:这就是为什么您不以明文形式发送散列。服务器向客户端发送一个随机盐,您附加密码哈希,然后再次对整个内容进行哈希处理,然后将其发送回执行相同计算的服务器。重放攻击失败,因为盐会不同 这个问题不应该在 security.stackexchange.com 上结束吗? 【参考方案1】:基本上,你的朋友是对的。但是在客户端简单地对密码进行哈希处理只是只是比将其作为纯文本提交给服务器更好。可以侦听您的纯文本密码的人当然也能够侦听散列密码,并使用这些捕获的散列值自己对您的服务器进行身份验证。
对于这个问题,更安全的身份验证协议通常会跳过许多环节,以确保这样的重放攻击无法起作用,通常是通过允许客户端选择一堆随机位,这些位被散列连同密码,并以明文形式提交给服务器。
在服务器上:
生成几位随机数 将这些位(以明文形式)发送给客户端在客户端:
生成一些随机位 连接密码、服务器随机位和客户端随机位 生成上述哈希 向服务器提交随机位(以明文形式)和哈希由于服务器知道自己的随机信息以及客户端的随机位(它以明文形式获取它们),它可以执行基本相同的转换。该协议确保,只要双方每次生成不同的“噪声位”,任何人都可以在以后使用该信息来使用记录的信息进行虚假身份验证(除非使用了非常弱的算法......),进行握手。
编辑所有这些都容易出错且乏味,并且有些难以正确处理(阅读:安全)。如果可能的话,考虑使用已经由知识渊博的人编写的身份验证协议实现(不像我!以上只是我前段时间读过的一本书的记忆。)你真的不想通常自己写这个。
【讨论】:
登录系统中的哈希密码会再次被哈希,他如何进行身份验证? 如果您将密码以散列形式存储在服务器上(正如您应该的那样),那么客户端将不得不对密码进行两次散列:首先,仅密码,因此它与服务器的视图相匹配世界,然后为了协议的缘故如上所述。 @Dirk,由于攻击者可以双向嗅探,“随机位”将被嗅探。然后攻击者可以离线分析(阅读:暴力破解)请求和响应对以找到原始密码。 但是,为了提交密码或密码的哈希值,通常使用像 Diffie-Hellman 这样的非对称过程来建立加密密钥,允许双方在没有窥探方的情况下交换信息能够解密它。这正是 SSL 所做的,也是大多数网站在 HTTPS/SSL 上拥有其登录页面的原因。 SSL 已经可以防止重放攻击。我会建议利用 SSL 而不是构建自己的协议。虽然我同意在密码客户端加盐+散列,但通过已建立的 SSL 连接发送这些。 要明确一点:如果您不使用 HTTPS,那么您在客户端上运行什么 javascript 并不重要; MITM 将能够在页面到达客户端之前对其进行修改。 HTTPS 是唯一的解决方案。【参考方案2】:首先,这不会提高您的应用程序的安全性(假设它是一个 web 应用程序)。使用 SSL(或者实际上是 TLS ,通常称为 SSL),它并不昂贵(测量您用来寻找解决方法的时间并将其与最低工资相乘,购买证书几乎总是获胜)。
这样做的原因很简单。 TLS 解决了一个在密码学中相当大的问题(当与购买的证书一起使用时,不是自签名的):我如何知道我正在与之交谈的服务器是我认为正在与之交谈的服务器? TLS 证书是一种说法:“我,证书颁发机构,受您的浏览器信任,证明 [url] 处的网站具有此公钥,以及相应的私钥,该私钥(私钥)只有服务器知道,看我在整个文件上都签名了,如果有人更改了,你可以看到”。
没有 TLS,任何加密都变得毫无意义,因为如果我在咖啡店坐在你旁边,我可以让你的笔记本电脑/智能手机认为我是服务器,而 MiTM(中间人)你。使用 TLS,您的笔记本电脑/智能手机会尖叫“不受信任的连接”,因为我没有与您的站点匹配的证书颁发机构签名的证书。 (加密与身份验证)。
免责声明:用户倾向于点击这些警告:“不受信任的连接?什么?我只想要我的小猫照片!添加例外 单击 确认 点击耶!小猫!”
但是,如果您真的不想购买证书,仍然实施客户端 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 应用程序的个人资料/设置中找到)。
【讨论】:
我认为这是一个很好的答案,但它可能需要更多关于如何正确加盐的信息,并且在将密码存储在服务器上之前最好使用“慢”散列函数(如 bcrypt) . 是的,不要直接使用 SHA256,而是使用 bcrypt 甚至更安全的 argon2 实现。【参考方案3】:您可能不必担心这一点 - 正如 Dirk 所提到的,即使您对密码进行哈希处理,恶意用户也可能在网络上并看到哈希被发送,并且可以简单地自己发送相同的哈希。
稍微更好,因为它可以防止恶意用户知道密码是什么,但由于他们仍然可以登录(或者可能是reconstruct the original password),所以这没什么帮助。
一般来说,如果您担心用户密码和数据的安全(而且您应该这样做!),您会希望使用安全的 SSL 服务器。如果无论出于何种原因这对您来说都不是问题,那么您最好不要打扰散列;只是security through obscurity。
2014 年 8 月编辑:Google 越来越强烈地将网站推向 switch to HTTPS everywhere,因为保护通信本身是防止网络嗅探攻击的唯一方法。混淆传输数据的尝试只会阻碍而不是阻止专门的攻击者,并且会给开发人员带来危险的错误安全感。
【讨论】:
我认为,如果您仅专注于获取x
用户的密码并访问单个服务,那么您认为客户端散列只是“稍微好一点”的观点是正确的。如果考虑集体效应,我会说它“好多了”;因为它可以防止构建用于在多个服务中暴力破解加盐哈希的大型密码查找数据库。国际海事组织。查看 AWS Cognito 在客户端中的作用以供参考。
我迟到了,但基于@f1lt3r 的回答,我想阐明一个事实,即用户经常对多个服务使用相同的密码,或者至少有一个清晰的图案。就我个人而言,过去我选择在客户端对密码进行哈希处理,只是为了让我的用户放心,我的服务器/我自己不知道他们的密码是什么【参考方案4】:
实际上,我不同意客户端散列在这种情况下更安全。我认为它不太安全。
在数据库中存储密码散列而不是真实密码(甚至是加密密码)的全部意义在于,在数学上不可能从散列中获取原始密码(尽管理论上可以获得一个冲突的哈希输入,其难度取决于哈希算法的安全强度)。这里可能的攻击向量是,如果潜在的攻击者以某种方式破坏了您的密码存储数据库,他/她仍然无法获得您用户的原始密码。
如果您的身份验证机制发送密码的哈希值,那么在这种安全漏洞场景中,攻击者不需要知道真正的密码 - 他们只需发送他们拥有的哈希值,嘿,他们可以访问特定的用户的帐户,以及您的整个系统。这完全违背了存储哈希密码的初衷!
真正安全的方法是向客户端发送一次性公钥,让他们加密密码,然后在服务器端解密并重新散列。
顺便说一句,这类问题可能会在 Security StackExchange 上得到更多专家的回答。
【讨论】:
完全同意这一点。对于客户端的工作方法,还必须将盐发送给客户端,这将使加盐的整个目的无效! @JamesWright:重点是为每次使用发送一个随机盐,以防止非法重用登录消息。 (重放攻击的一种形式)。每次都送同样的盐确实没有意义。 您需要做的是使用“nonce”(en.wikipedia.org/wiki/Cryptographic_nonce)。这样,服务器也可以控制盐并使其安全。客户端的散列也很重要,这样注入的 JS 代码以后就不能轻易找到它。更多信息:***.com/a/21716654/43615 显然,如果您在客户端对其进行散列,则必须在服务器端再次对其进行散列,以防止您提出的观点。正如其他人指出的那样,为了隐私,您已经需要客户端加密。 @user1034912 是的,但我认为 OP 不仅想依赖 SSL 来保证安全性。一些网络,尤其是企业网络,在某些负载平衡器处终止 SSL,这意味着明文密码对服务应用程序以外的网络组件可见。通常我们使用客户端加密来避免这种情况,并确保客户端和服务应用程序之间的 e2e 加密。【参考方案5】:请注意,保护密码免受第三方的侵害并不是全部。
一旦涉及隐私(现在什么时候不涉及?)你不想知道密码。你不能滥用或泄露你没有拥有的东西,所以如果你永远看不到他们的明文密码,你和你的客户都可以睡得更好。
因此,散列/加密客户端是有意义的。
【讨论】:
同意!很多人都忽略了这一点。如果我下载了您的加盐哈希密码的密码数据库,并且我可以使用哈希查找数据库仅破解一个,那么我很可能可以全部破解。一旦我有了 50k 原始密码,我现在就拥有了x
用户在 n
服务上的密钥,这些服务只在服务器上加密。如果每个服务在离开客户端之前唯一地对密码进行哈希处理,那么跨服务密码的大型数据库就会变得非常非常小。检查您的 AWS 登录流量,看看他们做了什么。这是概率,我亲爱的华生。
我完全同意,由于许多原因,在客户端散列更加合乎逻辑。如果黑客想知道实际的散列密码,他们将很难弄清楚。通过“安全”连接发送明文密码是完全愚蠢和愚蠢的。在黑客世界中没有所谓的“安全”连接。在用户端对用户密码进行散列可以带来新的安全算法,这对于黑客来说非常难以通过从服务器触发检测来假装自己是实际用户。【参考方案6】:
最近 GitHub 和 Twitter 都宣布密码存储在内部日志中。我在错误报告和其他进入 splunk 等的日志中无意中发生了这种情况。对于 twitter,如果特朗普的密码在日志中,那么管理员“看到”可能是一件大事,因为其他网站可能不像很重要,因为管理员不会有太多用处。无论如何,作为管理员,我们不喜欢看到密码。
所以问题是,为了安全起见,是否应该在客户端进行哈希处理,但我们如何才能在密码最终被服务器端进行哈希处理和比较之前保护密码,这样它就不会以某种方式被记录。
加密不是一个坏主意,因为开发人员至少必须跳过一些障碍,如果您发现密码进入日志,您只需更改加密密钥,销毁原始数据,然后这些数据就变得无用了。最好每晚轮换一下按键,这样可以大大减少窗户。
您还可以在您的用户记录中散列哈希值。泄露的密码将是经过哈希处理的纯文本密码。服务器将存储散列的散列版本。当然散列成为密码,但除非你有过目不忘的记忆,否则你不会记住一个 60 字符的 bcyrpt。用用户名加盐。如果您可以在登录过程中收集有关用户的一些信息(同时不暴露用户记录存在),您可以使用它来创建一个更强大的哈希值,该哈希值不能在站点之间共享。中间没有人能够在站点之间剪切和粘贴捕获的哈希。
与不会提交回服务器的 cookie 结合使用,您可能会遇到一些问题。在第一次请求时,使用密钥向客户端提交 cookie,然后确保 cookie 不会返回登录服务,因此被记录的可能性很小。将密钥存储在会话存储中,然后在登录发生或会话过期后立即将其删除...这需要 JWT 人员的状态,但可能只使用 nosql 服务。
因此,管理员在 splunk 或错误报告工具中遇到了这些散列和加密密码之一。这对他们来说应该是没用的,因为他们再也找不到加密密钥了,即使他们找到了,他们也必须暴力破解哈希。此外,最终用户并没有发送任何明文,因此中间的任何人至少都比较困难,你不能只是跳到另一个站点并登录。
【讨论】:
正是我关心的问题。如果服务器由于执行不善或恶意意图意外登录,那么如果他们重复使用密码,则用户的其他帐户可能会受到损害。 如果您使用从密码客户端生成的哈希进行身份验证,您也不希望在日志中看到它。【参考方案7】:这取决于,您可以使用客户端散列,但服务器端盐将不可能。
看看链接 Password encryption at client side
【讨论】:
【参考方案8】:客户端散列的这种想法是为了保护用户,而不是您的网站。正如已经多次提到的,纯文本或散列密码都可以平等地访问您的网站。您没有获得安全利益。
但是您的用户实际的纯文本密码应该只有他们自己知道。知道他们选择什么作为密码是可以在其他站点和系统上用来对付他们的信息。通过保护他们不让您的服务器开发人员或第三方发现他们的密码选择,您正在成为一个以客户为中心的网站。
【讨论】:
【参考方案9】:我最近在这方面做了很多工作,IRL 客户端哈希 / 对称 加密存在两个问题,这确实扼杀了这个想法: 1.您必须以某种方式将盐送回服务器……并在客户端对其进行加密,您需要一个密码……这违背了目的。 2. 你公开了你的散列实现(不是一个巨大的交易,因为大多数网站使用 3 或 4 个散列算法之一)这使得攻击更容易(因为只需要尝试一个而不是 n)。
我最终使用 OpenPGP.js 或类似方法在客户端进行非对称加密... 这依赖于客户端上的导入或客户端生成的密钥以及发送它的公钥的服务器。只有客户端的公钥可以发送回服务器。 这可以防止 MIM 攻击,并且与设备一样安全(我目前默认将客户端的私钥存储在 localStore 中,这是一个演示)。
这样做的主要优点是我永远不必存储用户数据/即使在我的服务器/数据存储上未加密的内存中(并且我的服务器的私钥在物理上是独立的)
这样做的基础是为人们提供一种在 HTTPS 受到限制的情况下(例如,伊朗/北韩 atc...)进行安全通信的方式,同时也是一个有趣的实验。
我第一次想到这个,http://www.mailvelope.com/ 使用这个
【讨论】:
【参考方案10】:如果有人可以在您的连接上看到进出数据,那么身份验证将无法拯救您。 相反,我会为超级秘密的东西做以下事情。
密码在发送到服务器之前在客户端进行了预哈希处理。 (服务器存储从浏览器发送的该哈希的另一个哈希和加盐值)。
因此,中间人攻击可以让他们发送与他们相同的散列值登录,但不会知道用户密码。这将阻止他们尝试使用相同凭据的其他服务在其他地方登录。
用户数据也在浏览器端加密。
啊,所以中间人攻击会得到加密数据,但如果没有用于登录的实际密码,就无法解密它。 (用户登录时存储在浏览器 DOM 中的密码)。 所以真正的用户会看到解密的内容,但中间人看不到。 这也意味着任何 NSA 或其他机构都无法要求您/公司/托管服务提供商解密这些数据,因为他们也不可能这样做。
这两种方法的一些小例子都在我的博客上 http://glynrob.com/javascript/client-side-hashing-and-encryption/
【讨论】:
【参考方案11】:考虑一下:-
客户端向服务器发送请求“我有密码要验证”。
服务器向客户端发送一个一次性的随机字符串。 R$
客户端在此字符串中嵌入用户密码(基于您希望应用的任何(可变)规则)。
客户端将字符串发送到服务器,如果密码OK,服务器将用户登录。
如果服务器收到另一个使用 R$ 的登录请求,用户将被注销并且帐户被冻结等待调查。
显然会采取所有其他(正常)安全预防措施。
【讨论】:
以上是关于是否值得在客户端散列密码的主要内容,如果未能解决你的问题,请参考以下文章
当我在客户端散列密码时,我没有得到散列字符串,而是获取密码本身
是否有任何通常被认为值得信赖的 SHA-256 javascript 实现?