脑筋急转弯:为了防止运维人员能够直接去数据库里查看密码,Web应用普遍采用将密码哈希一趟后再存入数据库。需要校验时,由前端提供密码值,服务器将密码哈希后再比对,从而判断密码是否正确。这样运维人员即使接触的到数据库,也最多只能重置密码,无法登录一个特定的账号。

那么,既然最终需要比对的是密码的哈希,为何不直接在前端哈希完,再直接把哈希后的结果发给服务器,来鉴定密码是否正确呢?

答案其实挺简单的。当初我们加一次哈希是为了阻止运维登录。而如果哈希在前端完成,那么对于服务器而言,它其实只是做了一次明文的比较。数据库里的哈希值就相当于密码了。运维完全可以直接拿着哈希去调登录的API来获取一个账号的权限。

这个脑筋急转弯挺有趣的,乍一看仿佛还挺有道理的。但是换句话说,虽然看起来前端加哈希蠢蠢的,其实历史上有很多公司和项目都做过类似的事情。其中比较典型的就是腾讯公司的QQ了。

在十几年前的时候,HTTPS并不普及,而HTTP的协议特征明显、内容可以直接抓包截获,这就意味着,如果直接前端把密码明文发给了后端,非常容易遭到中间人攻击从而盗取QQ号的密码。所以腾讯为了解决这个问题,就曾经在一段时间内采用了两趟哈希的做法:首先在前端把密码进行第一趟哈希,再传给服务器。服务器拿到第一趟哈希的结果后,再进行第二趟哈希,去和数据库里的两趟哈希进行比对,来鉴定密码。

这样做看起来能够同时解决运维直接登录账号的问题,也能解决中间人窃取密码的问题。但是它实质上对于防范中间人攻击并没有什么帮助。如果黑客能够意识到上面这件事,就可以非常轻松的拿着第一趟哈希的结果去调用API。同理,这一趟加密为了避免中间人攻击,靠哈希不靠谱,靠对称加密算法也不靠谱,因为只要是能够被抓到了的包,就是能够被伪造的包。

我第一次了解到这件事,是因为在那个年代几乎所有的信息安全方面考虑的都较差。那时Windows XP里有一个非常常见的网络小工具叫WinPCap,可以实现ARP欺骗的效果。再加上我的高中全校的所有机器都在一个子网里(整个学校只有两个机房和几个办公室配有电脑),仅仅在上微机课的时间里就可以靠ARP欺骗抓到大量密码。其中不少我发现都是QQ密码,但是拿去登录QQ号并不能成功。当时我并没有意识到它的加密并不止一趟。依稀记得当时其中抓到的能用的密码,最多的是Email的密码。主要是SMTP协议特征太明显,端口好识别,包一眼就能发现特征,而当时也没有任何加密措施,往往一晚上拿到好多Email密码,就可以去百度贴吧(当时2012年时我们学校主要社区还是百度贴吧)去尝试用这个Email找回百度账号的密码,进而就能掌控一个人几乎所有的线上资料。

这些事儿在那个年代易如反掌,而在今天,中间人攻击在RSA算法普及后已经几乎不可能成功。这是因为即使你抓到了可用的包,但TLS的通信不止一个包,你在完全完成之前的握手前你的包根本用不了。Windows也从底层禁止了类似的ARP欺骗。今天开发Web应用,只借助TLS,然后把密码原文直接发送给服务器已经相当安全了。

真的……这么安全吗?有人曾经质疑我,说他们使用过可以拦截HTTPS流量的拦截软件,例如Wireshark。

拦截HTTPS流量的软件,靠的其实只是我以前介绍过的反向代理。在被攻击者误以为他在直接和目标服务器建立TLS通信时,实际上是建立了到Wireshark的TLS通信。Wireshark承载服务器的能力捕获这个请求后,再将原文反向发送给目标请求的服务器,从而捕获原文。

完整的TLS通信中,不但会验证这张证书本身是否合法,也会验证签发它的机构是否合法。Wireshark在安装时会将其本身安装为受信任的机构,从而能够合理的绕过这个过程,因此除了能截获自己电脑的HTTPS流量,截别人的且不谈解码有多难,ARP欺骗在今天也困难得多了。

当然,我们今天远不能完全信任HTTPS。通信层面的防窃取,往往还引出了更幼稚却有效的攻击手段。例如Chrome浏览器的插件是有权力直接阅读网页表单而无视其它任何通信层面的安全措施的。安装上了一个假冒的插件可能会带来真正的威胁,尤其在新Edge的插件生态不成熟的情况下,盲目安装插件的风险也相当的高。(我前几天就差点装了诈骗版本的Dark Reader)以及更令人无语的:把网页做的和QQ看起来一模一样,每天就可以骗到大把的QQ号密码等。真正解决这些问题,仅靠安全措施和算法都远远不够。提高普通用户的警觉性和判断能力,以及对基础安全领域的常识才是根本。