当我想建立一个登录系统时,我总是将给定密码的MD5与服务器端用户表中的值进行比较。
然而,我的一个朋友告诉我,一个“清晰”的密码可能会被网络软件嗅到。
所以我的问题是:在客户端散列密码是个好主意吗?它比在服务器端散列更好吗?
基本上,你的朋友是对的。但是,简单地在客户端散列密码只比将其作为纯文本提交给服务器更好。能够收听您的纯文本密码的人当然也能够收听散列密码,并使用这些捕获的哈希来对您的服务器进行身份验证。
对于这个问题,更安全的身份验证协议通常会跳过许多箍,以确保这种重放攻击通常不会起作用,通常是允许客户端选择一堆随机位,这些位随密码一起被散列,并且还明确提交给服务器。
在服务器上:
在客户端:
由于服务器知道自己的随机信息以及客户端的随机位(它将它们作为明文),它可以执行基本相同的转换。该协议确保在此对话中的任何人都可以使用该信息稍后使用所记录的信息进行错误验证(除非使用了非常弱的算法......),只要双方每次都生成不同的“噪声位”,进行握手。
编辑所有这一切都是容易出错和繁琐而且有点难以做到(阅读:安全)。如果可能的话,考虑使用已经由知识渊博的人编写的身份验证协议实现(与我不同!以上仅仅是我之前读过的一本书的记忆。)你真的不想自己写这个。
考虑一下: -
客户端向服务器发送请求“我有一个密码来验证”。
服务器向客户端发送一次性随机字符串。 R $
客户端将用户密码嵌入此字符串中(基于您希望应用的任何(变量)规则)。
客户端将字符串发送到服务器,如果密码正常,则服务器将用户登录。
如果服务器使用R $接收另一个登录请求,则用户将被注销,并且该帐户将被冻结等待调查。
显然,将采取所有其他(正常)安全预防措施。
首先,这不会提高应用程序的安全性(假设它是一个webapp)。 使用SSL(或者实际上是TLS,通常称为SSL),它并不是真的很贵(衡量你用来寻找解决方法的时间并用最低工资乘以它,购买证书几乎总能获胜)。
为什么这很简单。 TLS解决了一个问题(当与购买的证书一起使用,而非自签名时)在密码学方面相当大:我如何知道我正在与之交谈的服务器是我认为我正在与之交谈的服务器? TLS证书是一种说法:“我,您的浏览器信任的证书颁发机构,证明[url]上的网站有这个公钥,带有相应的私钥,只有服务器知道的(私钥),看看我在整个文档上签了我的签名,如果有人改了你就可以看到“。
没有TLS,任何加密都变得毫无意义,因为如果我坐在你旁边的咖啡店,我可以让你的笔记本电脑/智能手机认为我是服务器和MiTM(中间人)你。使用TLS,您的笔记本电脑/智能手机会尖叫“UNTRUSTED CONNECTION”,因为我没有与您的网站匹配的证书颁发机构签名证书。 (加密与身份验证)。
免责声明:用户倾向于点击这些警告:“不受信任的连接?什么?我只想要我的小猫照片!添加例外点击确认点击YAY!小猫!”
但是,如果你真的不想购买证书,仍然要实现客户端javascript哈希(并使用standford库(SJCL),不要实施CRYPTO自己)。
为什么?密码重用!我可以在没有HTTPS的情况下窃取您的会话cookie(这允许我假装您的服务器,我是你)(参见firesheep)。但是,如果你在你的登录页面添加一个javascript,在发送之前,哈希你的密码(使用SHA256,甚至更好,使用SHA256,向他们发送你生成的公钥,然后加密密码,你不能使用盐用此),然后将散列/加密的密码发送到服务器。使用salt重写服务器上的哈希并将其与存储在数据库中的哈希进行比较(存储密码如下:
(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,但是,星巴克黑客可能不会知道如何/有兴趣在这。)所以他们将获得访问您的webapp,但不能访问他们的电子邮件/脸书/等。 (您的用户可能会使用相同的密码)。 (电子邮件地址将是他们的登录名,或者将在他们的webapp上的个人资料/设置中找到)。
您可能不必担心这一点 - 正如Dirk所提到的那样,即使您使用恶意用户在网络上散列密码并看到散列被发送,也可以简单地自己发送相同的散列。
它稍微好一些,因为它可以防止恶意用户知道密码是什么,但由于他们仍然可以登录(或可能是reconstruct the original password),这没有那么有用。
通常,如果您担心用户的密码和数据的安全性(并且您应该这样做!),您将需要使用安全的SSL服务器。如果出于某种原因这不是你的问题,你也可以不去烦恼;它只是security through obscurity。
编辑2014年8月:Google正在越来越强烈地将网站推向switch to HTTPS everywhere,因为保护通信本身是防止网络嗅探攻击的唯一方法。混淆传输数据的尝试只会阻碍而不是阻止专用攻击者,并且会给开发人员带来危险的虚假安全感。
实际上我不同意客户端哈希在这种情况下更安全。我认为它不太安全。
在数据库中存储密码哈希而不是真实密码(甚至是加密密码)的全部意义在于,在数学上不可能从哈希中获取原始密码(尽管理论上可以获得冲突)散列输入,其难度取决于散列算法的安全强度)。这里可能的攻击媒介是,如果潜在的攻击者以某种方式破坏了您的密码存储数据库,他/她仍然无法获得您的用户的原始密码。
如果您的身份验证机制发送密码哈希值,那么在此安全漏洞情况下,攻击者无需知道真实密码 - 他们只是发送他们拥有的哈希值,而且他们可以访问特定用户的帐户,通过扩展,您的整个系统。这完全打败了存储散列密码的重点!
真正安全的方法是向客户端发送一次性公钥,以便加密密码,然后在服务器端对其进行解密和重新哈希。
顺便说一下,这类问题可能会在Security StackExchange上得到更多专家的回应。
这取决于你可以使用客户端哈希,但服务器端盐是不可能的。
请注意,保持密码对第三方的安全并不是全部。
一旦涉及隐私(这些日子什么时候不存在?)你不想知道密码。你不能滥用或泄露你没有的东西,所以如果你从未见过他们的明文密码,你和你的客户都可以睡得更好。
因此,哈希/加密客户端是有道理的。
我最近在这方面做了很多工作,IRL有两个问题,客户端散列/对称加密真的杀了这个想法:1。你必须让盐回到服务器SOMEHOW ...并加密客户端你需要一个密码......这会破坏目的。 2.你暴露你的哈希实现(不是一个巨大的交易,因为大多数网站使用3或4个哈希算法中的一个),这使得攻击更容易(因为只需要尝试一个而不是n)。
我最终得到的是使用OpenPGP.js或类似的客户端上的非对称加密...这依赖于客户端上的导入或客户端生成的密钥以及发送它的公钥的服务器。只有客户端的公钥可以发送回服务器。这可以防止MIM攻击,并且与设备一样安全(我目前默认在localStore中存储客户端的私钥,这是一个演示)。
这样做的主要优点是我永远不必将用户数据存储在我的服务器/数据存储中未加密的存储器中(并且我的服务器的私钥在物理上是分开的)
这样做的基础是为人们提供一种在HTTPS被限制的地方安全通信的方式(例如,伊朗/朝鲜国家...),这也只是一个有趣的实验。
我是第一个考虑到这一点的人,http://www.mailvelope.com/使用它
如果有人可以在您的连接上看到数据,那么身份验证将无法为您节省时间。相反,我会为超级秘密的东西做以下事情。
在发送到服务器之前在客户端上预先存储的密码。 (服务器存储从浏览器发送的该哈希的另一个哈希值和盐值)。
因此,中间人攻击可以允许他们发送相同的哈希值来登录,但是用户密码不会被知道。这将阻止他们尝试使用相同凭据的其他服务来登录其他地方。
用户数据也在浏览器端加密。
啊,所以中间人攻击会得到加密数据,但如果没有用于登录的实际密码就无法解密。 (用户密码在登录时存储在浏览器的DOM中)。所以真正的用户会看到解密的内容,但中间人不能。这也意味着任何NSA或其他机构都无法要求您/公司/托管服务提供商解密这些数据,因为他们也不可能这样做。
这两种方法的一些小例子都在我的博客http://glynrob.com/javascript/client-side-hashing-and-encryption/上
最近GitHub和Twitter都宣布密码存储在内部日志中。我已经无意中发生了错误报告和其他日志,这些日志已经进入了splunk等。对于twitter,如果特朗普的密码在那里日志,对于管理员“看”可能是一个大问题,对于其他网站可能不是由于管理员对它没有多大用处,所以大不了一笔。无论管理员如何,我们都不喜欢看密码。
所以问题是,如果哈希应该在客户端发生安全性,但是如何在最终获得哈希值并在服务器端进行比较之前保护密码,以便不以某种方式记录。
加密并不是一个坏主意,因为开发人员至少必须跳过一些箍,如果你发现密码进入日志,你只需更改加密密钥,破坏原始密码,数据就变得无用了。更好的是每晚旋转按键,它可以大大减少窗户。
您还可以在用户记录中散列哈希值。泄露的密码将是纯文本密码。服务器将存储哈希的哈希版本。确定哈希成为密码,但除非你有一个摄影记忆,否则你不会记得一个60 char bcyrpt。使用用户名加盐。如果您可以在登录过程中收集有关用户的信息(虽然不公开用户记录存在),您可以使用它,并创建一个无法在站点之间共享的更健壮的哈希。中间没有人能够在网站之间剪切和粘贴捕获的哈希。
与未提交回服务器的cookie结合使用,您可能会遇到某些问题。在第一次请求时,使用密钥向客户端提交cookie,然后确保cookie不会返回登录服务,因此很少有机会记录它。将密钥存储在会话存储中,然后在登录发生后或会话过期时立即将其删除...这需要为JWT人员提供状态,但可能只是使用nosql服务。
因此,管理员会在splunk或bug报告工具中遇到其中一个散列和加密密码。对他们来说应该没用,因为他们再也找不到加密密钥,即使他们这样做了,他们也必须暴力破解哈希。此外,最终用户没有沿线发送任何明文,因此中间的任何人至少有更难的时间,你不能只是跳到另一个站点并登录。