简单分析:QQ 邮箱是如何发送你的密码的

qq

今天对腾讯这样的大厂如何发送用户密码产生了一些兴趣,简单看了一下, 记录一下看到的东西,首先声明只是个人兴趣,没有意愿和实力完整分析详细过程, 只是供后续参考

前言

自己写一些简单 web 应用的时候,用户密码我的常用做法一直是把原文发送到服务端, 安全方面的同事会认为这样做是有问题的,我是觉得这样的问题也不大, 因为既然启用了 https,那么中间人是看不到我的请求内容的, 而且即使我在发送前对密码做一些加密,如果有人想了解, 他也可以通过分析网页代码来确定我是如何加密的,所以对于精力有限的项目, 在这上面花费时间我觉得意义不大,不过偶尔我也会想,大厂是会如何处理这种情况? 今天我们看一下腾讯的 qq 邮箱是怎么处理的吧,不过因为本人水平有限, 也就只能简单看一下

使用到的工具

  • FireFox(F12 开发者模式)

分析

密码框

qq 邮箱登陆页

qq 邮箱密码框代码

密码框倒是没啥稀奇的,就是一个类型设为 password<input>nameid 均为 p,至于为啥说没啥稀奇,因为之前看到一个密码的处理方案, 他也是一个类型为 password<input>,但是每当你按一个键, input 并不会保存你的密码,而是保存一个「*」,这个就稀奇了, 我也不知道这么做的用意是什么,不过这不是我们今天要讨论的,就不再继续深究了。

登陆流程

我们有两个着手点,来去看登陆是如何进行的,一个是检查点击登陆按钮时, 系统是如何相应点击事件的,另一个是看点击按钮后,系统发送了什么网络请求。

点击事件的处理

我们用 inspector 查看登陆按钮,即可看到点击事件的代码位置:

登陆按钮的点击事件代码位置

接下来在点击开始时打下断点:

点击登陆按钮时中断

网络请求

这样,我们输入账号密码点击登陆时,程序就会中断,这时我们要清空网络访问请求日志, 再放开断点,检查之后浏览器都发送了哪些请求:

点击登陆后的网络请求

看上去蓝色的这条请求最像是发送给服务端的登陆请求,我们可以看到我们的账号, 但是看不到我们的密码,所以应该是发送前,密码被以某种形式加密了, 我们可以点击 initiator 中的代码位置,看看源码:

loadScript 源码

这是一段「奇技淫巧」的代码,首先这个函数被定义为一个属性, 然后函数的功能是在 head 处,添加一个 <script> 的标签, 然后标签的 src 属性被设为传入参数 t,所以 t 就是请求的 url, 但是这个请求不是通过常规的 XHR 请求,而是加入一个 <script> 标签, 然后利用浏览器的特性去加载这个请求,所以如果我们打算用浏览器自带的 「Pause on any URL」的 XHR 断点,那么很抱歉,程序不会中断在这里。

stacktrace

我们回到火狐 network 页签,看一下登陆请求的 stacktrace:

登陆请求的 stacktrace

既然 loadScript 是发送登陆请求的位置,那么我们也可以推断就是 submiturl 传递给 loadScript 的,看一下他的代码吧,2981 行,我们看到了这个: k['default'].http.loadScript(t),所以 t 就是 url,那么 t 是怎么得来的呢:

...
clearTimeout(T.loginClock),
T.loginClock = setTimeout('pt.plogin.loginTimeout();', 5000);
t = T.getSubmitUrl('login');
return T.smsLoginUrl = t,
k['default'].winName.set('login_href', encodeURIComponent(C.ptui.href)),
T.showLoading(),
...

getSubmitUrl

看来接下来我们要找 getSubmitUrl,这次我们要用搜索:

getSubmitUrl 的源码

又是一段非常难懂的代码,但是我们离答案已经很近了,我们可以在这里下一个断点, 验证一下是不是登陆时会走到这里,我这里就略过了,答案是「是」,所以这里一定是 生成 url 的地方,2954 行的这个 for 已经超出了我的理解范围,不过还好我看到了这个: e = (0, k['default']) ('p').value

「p」我们还记得就是密码框的 id,我们可以在 console 下面看看 (0, k['default']) 是什么:

(0, k['default']) 是什么

居然是个函数,点小箭头我们就可以看到定义:

var $ = function $(t) {
    return 'string' == typeof t ? document.getElementById(t) : t
},

所以我们大概可以猜到 (0, k['default']) ('p').value 是个什么,没错, 就是我们的密码,所以这个 for 就是算出 url 所有参数的地方。

结论

密码是如何加密的,我就不再往下继续了,看这个 for 真的很头大, 我也不怎么感兴趣,就算研究出来估计也不好发表,就到这里吧, 而且我觉得我的目标基本也已经达到了,虽然不太严谨,但是基本我们可以认定, qq 邮箱的做法也是发送前,对用户的密码进行了某种加密。

同时我觉得也从另一方面说明,这个加密真的是跟我最早想得差不多,如果你想知道, 你一定可以知道的,某种程度上也算是防君子不防小人吧。