在客户端是否值得哈希密码?

当我想放置一个login系统时,我总是比较给定密码的MD5和它在服务器端用户表中的值。

然而,我的一个朋友告诉我,一个“清晰”的密码可以被networking软件嗅探到。

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

基本上,你的朋友是对的。 但是在客户端简单地散列密码只不过把它作为纯文本提交给服务器。 有人可以监听纯文本密码,当然也可以监听散列的密码,并使用这些捕获的散列本身来对服务器进行身份validation。

对于这个问题,更安全的身份validation协议通常会跳过一些环节,以确保这样的重放攻击无法正常工作,通常情况下,允许客户端select一串随密码一起散列的随机位,并且也清楚地提交给服务器。

在服务器上:

  • 生成几个随机的位
  • 将这些位(以明文forms)发送到客户端

在客户端:

  • 产生一些随机比特
  • 连接密码,服务器的随机位和客户端随机位
  • 生成以上的哈希
  • 提交随机数据(明文)和哈希到服务器

由于服务器知道它自己的随机信息以及客户端的随机比特(它将它们作为明文),它可以执行基本相同的转换。 这个协议可以确保在这个对话中没有人听到后来可以使用这些信息来使用logging的信息进行错误的validation(除非使用非常弱的algorithm),只要双方每次产生不同的“噪声位”握手被执行。

编辑所有这一切都是容易出错和乏味的,有些难以理解(阅读:安全)。 如果可能的话,考虑使用已经由有知识的人编写的authentication协议实现(不像我!以上仅仅是我前一段时间读的一本书的记忆。)你真的不想自己写这个通常。

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

使用SSL (或者实际上是TLS,通常被称为SSL),它并不是很昂贵(衡量你用来寻找方法的时间,并用最低工资乘以它,几乎总是购买证书)。

为什么这很简单。 TLS解决了密码学中相当大的一个问题(当与购买的证书一起使用时,不是自签名的):我如何知道我正在与之交谈的服务器是我认为正在与之交谈的服务器? TLS证书是这样一种说法:“我的证书颁发机构(由您的浏览器信任)certificate[url]中的网站拥有该公钥,并具有相应的私钥,只有服务器知道哪个(私钥)我在文件上签了字,如果有人修改了,你可以看到“。

没有TLS,任何encryption变得毫无意义,因为如果我坐在咖啡店旁边,我可以让你的笔记本电脑/智能手机认为我是服务器和MiTM(中间人)你。 使用TLS,您的笔记本电脑/智能手机将尖叫“不可信的连接”,因为我没有与您的网站相匹配的authentication中心签名证书。 (encryption与authentication)。

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

但是,如果你真的不想购买证书,还是要实现客户端的JavaScript哈希 (并使用斯坦福库(SJCL), 永远不要执行CRYPTO YOURSELF )。

为什么? 密码重用! 我可以在不使用HTTPS的情况下轻松地窃取会话cookie(允许我假装自己是你的服务器)(请参阅firesheep)。 然而,如果你在你的login页面添加一个JavaScript,在发送之前,你的密码被哈希(使用SHA256,甚至更好,使用SHA256,发送一个你生成的公钥,然后encryption散列密码,你不能使用盐与此),然后发送散列/encryption的密码到服务器。 用盐把你的服务器上的散列重新encryption,然后把它和存储在你的数据库中的数据进行比较 (存储密码是这样的:

(SHA256(SHA256(password)+salt)) 

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

 RSA_With_Public_Key(SHA256(password)) 

并检查你的密码是这样的:

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

因为, 如果有人嗅探你的客户端,他们将能够login为客户端(会话劫持),但他们永远不会看到明文密码(除非他们改变你的JavaScript,但是,星巴克黑客可能不知道如何/有兴趣在这。)所以他们将获得访问您的Web应用程序,而不是他们的电子邮件/ Facebook /等。 (您的用户可能会使用相同的密码)。 (电子邮件地址将是他们的login名,或者可以在您的web应用程序的个人资料/设置中find)。

你可能不用担心这一点 – 就像Dirk提到的那样,即使你散列恶意用户在networking上的密码,并看到散列被发送,并且可以简单地发送相同的散列。

稍微好一些,因为它可以防止恶意用户知道密码是什么,但由于他们仍然可以login(或可能重build原始密码 ),所以这没有什么帮助。

一般来说,如果您担心用户密码和数据的安全(您应该!),您将需要使用安全的SSL服务器。 如果这不是由于任何原因你不关心你可能不打扰哈希; 这只是通过默默无闻的安全 。


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

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

将密码散列存储在数据库中而不是真实密码(或者甚至是encryption的密码)的整个要点是从math上不可能从散列获得原始密码(尽pipe理论上可能获得碰撞散列input,其难度取决于散列algorithm的安全强度)。 这里可能的攻击媒介是,如果一个潜在的攻击者以某种方式破坏了你的密码存储数据库,他/她仍然不能获得你的用户的原始密码。

如果您的身份validation机制发送了密码哈希值,那么在这种安全漏洞情况下,攻击者不需要知道真实密码 – 他们只是发送他们拥有的哈希值,他们可以访问特定的用户帐户,并通过扩展,整个系统。 这完全破坏了存储哈希密码的地步!

真正安全的方法是向客户端发送一次性公钥,以便他们encryption密码,然后在服务器端解密并重新哈希。

顺便说一下,这种问题可能会在安全StackExchange上得到更多的专家响应。

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

看看在客户端的密码encryption链接

我最近在这方面做了很多工作,IRL有两个与客户端散列/ 对称encryption有关的问题,真正杀死了这个想法:1.你必须把盐带回服务器SOMEHOW …并encryption它的客户端,你会需要一个密码…这是失败的目的。 2.你公开你的哈希实现(不是一个很大的交易,因为大多数网站使用3或4散列algorithm之一),这使得攻击更容易(只需要尝试一个而不是n)。

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

这样做的主要好处是,我永远不需要在我的服务器/数据存储上存储用户数据(即使在内存中),并且我的服务器的私钥在物理上是分开的

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

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

如果有人可以在您的连接上看到数据,那么身份validation不会为您节省。 相反,我会做以下超级秘密的东西。

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

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

用户数据也在浏览器端encryption。

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

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

你可以使用任何第三方库

jCryption

客户端encryption使用jCryption

考虑这个:-

客户端发送请求到服务器“我有一个密码来validation”。

服务器向客户端发送一次只有随机的string。 R $

客户端将用户的密码embedded到该string中(基于您希望应用的任何(可变)规则)。

客户端将string发送到服务器,如果密码确定,服务器将用户login。

如果服务器使用R $接收另一个login请求,用户将被注销,并且该帐户被冻结以待调查。

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