ChatGPT解决这个技术问题 Extra ChatGPT

是否值得在客户端散列密码

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

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

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

我正在考虑在客户端对密码进行哈希处理,但只是为了让我可以放心,客户端的密码永远不会在服务器端以明文形式存在,这意味着知道我不知道他们的实际密码,他们会感觉更轻松,或者如果受到损害,不能轻易放弃。我疯了吗?
只是为了完整起见,因为我们正在谈论安全性,并且 OP 中提到了 MD5:One should always use a salt when encrypting a password. 使用纯的、未加盐的 MD5 比在数据库中存储纯文本密码稍微好一点。
@Cyclone 仅在客户端散列绝对是一个坏主意,因为如果攻击者以某种方式知道散列,他可以使用它来登录,就好像他知道密码一样,绕过客户端散列代码。
@Teejay:这就是为什么您不以明文形式发送哈希的原因。服务器向客户端发送一个随机盐,您附加密码哈希,然后再次对整个内容进行哈希,然后将其发送回执行相同计算的服务器。重放攻击失败,因为盐会不同
这个问题不应该在 security.stackexchange.com 上结束吗?

i
imrok

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

为此,更安全的身份验证协议通常会跳过许多环节,以确保此类重放攻击无法正常工作,通常是允许客户端选择一堆随机位,这些位与密码一起进行散列,并且还以明文形式提交给服务器。

在服务器上:

生成一些随机数

将这些位(以明文形式)发送给客户端

在客户端:

生成一些随机位

连接密码、服务器的随机位和客户端的随机位

生成上述哈希

向服务器提交随机位(以明文形式)和散列

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

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


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

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

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

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

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

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

但是,如果您真的不想使用证书,仍然要实现客户端 Javascript 散列(并为此使用斯坦福库(SJCL),永远不要自己实现加密)。

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

(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。但是,星巴克黑客可能不知道如何或不感兴趣在这个。)所以他们将获得访问您的网络应用程序的权限,但不能访问他们的电子邮件/Facebook/等。 (您的用户可能会使用相同的密码)。 (电子邮件地址可以是他们的登录名,也可以在您的网络应用程序的个人资料/设置中找到)。


我认为这是一个很好的答案,但它可能需要更多关于如何正确加盐的信息,并且最好在将密码存储在服务器上之前使用“慢速”散列函数(如 bcrypt)。
是的,不要直接使用 SHA256,而是使用 bcrypt 甚至更安全的 argon2 实现。
A
Adam Burley

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

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

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

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

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


完全同意这一点。对于客户端的工作方法,还必须将盐发送给客户端,这将使加盐的整个目的无效!
@JamesWright:重点是为每次使用发送一个随机盐,以防止非法重用登录消息。 (重放攻击的一种形式)。每次发送相同的盐确实没有意义。
显然,如果您在客户端对其进行哈希处理,则必须在服务器端再次对其进行哈希处理,以防止您提出问题。正如其他人指出的那样,为了隐私,您已经需要客户端加密。
发送一次性公钥......这不是 SSL 的作用吗?
@user1034912 是的,但我认为 OP 不仅想依靠 SSL 来确保安全。某些网络,尤其是企业网络,在某些负载平衡器处终止 SSL,这意味着明文密码对服务应用程序以外的网络组件可见。通常我们使用客户端加密来避免这种情况,并确保客户端和服务应用程序之间的 e2e 加密。
d
dimo414

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

稍微好一点,因为它可以防止恶意用户知道密码是什么,但由于他们仍然可以登录(或可能是 reconstruct the original password),因此没有多大帮助。

一般来说,如果您担心用户密码和数据的安全(而且您应该这样做!),您会希望使用安全的 SSL 服务器。如果无论出于何种原因这对您来说都不是问题,那么您最好不要打扰散列;它只是 security through obscurity

2014 年 8 月编辑:Google 越来越强烈地推动网站使用 switch to HTTPS everywhere,因为保护通信本身是防止网络嗅探攻击的唯一方法。混淆传输数据的尝试只会阻碍而不是阻止专门的攻击者,并且会给开发人员带来危险的错误安全感。


如果您专注于获取 x 用户的密码并访问单个服务,我认为您认为客户端散列只是“稍微好一点”的观点是正确的。如果考虑集体效应,我会说它“好多了”;因为它可以防止构建用于在多个服务中暴力破解加盐哈希的大型密码查找数据库。国际海事组织。请参阅 AWS Cognito 在客户端中执行的操作以供参考。
我迟到了,但在@f1lt3r 的回答的基础上,我想阐明一个事实,即用户经常对多个服务使用相同的密码,或者至少有一个明确的模式。就我个人而言,过去我选择在客户端对密码进行哈希处理,只是为了向我的用户保证我的服务器/我自己不知道他们的密码是什么
R
Raphael

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

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

因此,散列/加密客户端是有意义的。


同意!很多人都忽略了这一点。如果我下载了您的加盐哈希密码的密码数据库,并且我可以使用哈希查找数据库仅破解一个,那么我很可能可以全部破解。一旦我有了 50k 原始密码,我现在就拥有了 n 服务上的 x 用户的密钥,这些服务只在服务器上加密。如果每个服务在离开客户端之前唯一地对密码进行哈希处理,那么跨服务密码的大型数据库就会变得非常非常小。检查您的 AWS 登录流量,看看他们做了什么。这是概率,我亲爱的华生。
我完全同意,由于许多原因,客户端的散列更加合乎逻辑。如果黑客想知道实际的散列密码,他们将很难弄清楚。通过“安全”连接发送明文密码是完全愚蠢和愚蠢的。在黑客世界中没有所谓的“安全”连接。在用户端对用户密码进行散列可以带来新的安全算法,这对于黑客来说非常难以通过从服务器触发检测来看似他们是实际用户。
E
Eric Twilegar

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

所以问题真的是散列是否应该发生在客户端以确保安全,但是我们如何在密码最终被服务器端散列和比较之前保护密码,这样它就不会以某种方式被记录下来。

加密不是一个坏主意,因为开发人员至少必须跳过一些障碍,如果您发现密码进入日志,您只需更改加密密钥,销毁原始密钥,然后这些数据就变得无用了。最好每晚旋转一下钥匙,这样可以大大减少窗户。

您还可以在用户记录中散列哈希。泄露的密码将是经过哈希处理的纯文本密码。服务器将存储散列的散列版本。当然哈希成为密码,但除非你有过目不忘的记忆,否则你不会记住 60 字符的 bcyrpt。用用户名加盐。如果您可以在登录过程中收集有关用户的一些信息(同时不暴露用户记录存在),您可以使用它来创建一个更强大的哈希值,该哈希值不能在站点之间共享。中间没有人能够在站点之间剪切和粘贴捕获的哈希。

结合不提交回服务器的 cookie,您可能会遇到一些问题。在第一次请求时,使用密钥向客户端提交 cookie,然后确保 cookie 不会返回登录服务,因此被记录的可能性很小。将密钥存储在会话存储中,然后在登录发生或会话过期后立即将其删除……这需要 JWT 人员的状态,但可能只使用 nosql 服务。

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


正是我的担心。如果服务器由于实施不善或恶意意图以某种方式意外登录,那么如果他们重用密码,则用户的其他帐户可能会受到损害。
如果您使用从密码客户端生成的哈希进行身份验证,您也不希望在日志中看到它。
C
Community

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

查看链接 Password encryption at client side


n
n8isjack

客户端散列的这种想法是为了保护用户,而不是您的站点。正如已经多次提到的,纯文本或散列密码都可以平等地访问您的网站。您没有获得安全利益。

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


S
ScottGal

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

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

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

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

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


G
GlynRob

如果有人可以在您的连接上看到进出数据,那么身份验证将无法拯救您。相反,我会为超级秘密的东西做以下事情。

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

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

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

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

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


c
cneeds

考虑一下:-

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

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

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

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

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

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


D
Daniel Rudy

我想出的方法是使用 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 地址和相同的用户代理。这都是关于概率的。此外,任何时候你在处理加密货币时,都很难做到正确。所以正如其他人所说,不要自己做。


t
theking2

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

我相信您会花时间熟悉这些技术。

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