今天对腾讯这样的大厂如何发送用户密码产生了一些兴趣,简单看了一下, 记录一下看到的东西,首先声明只是个人兴趣,没有意愿和实力完整分析详细过程, 只是供后续参考
自己写一些简单 web 应用的时候,用户密码我的常用做法一直是把原文发送到服务端, 安全方面的同事会认为这样做是有问题的,我是觉得这样的问题也不大, 因为既然启用了 https,那么中间人是看不到我的请求内容的, 而且即使我在发送前对密码做一些加密,如果有人想了解, 他也可以通过分析网页代码来确定我是如何加密的,所以对于精力有限的项目, 在这上面花费时间我觉得意义不大,不过偶尔我也会想,大厂是会如何处理这种情况? 今天我们看一下腾讯的 qq 邮箱是怎么处理的吧,不过因为本人水平有限, 也就只能简单看一下
密码框倒是没啥稀奇的,就是一个类型设为 password
的 <input>
,
name
和 id
均为 p
,至于为啥说没啥稀奇,因为之前看到一个密码的处理方案,
他也是一个类型为 password
的 <input>
,但是每当你按一个键,
input 并不会保存你的密码,而是保存一个「*」,这个就稀奇了,
我也不知道这么做的用意是什么,不过这不是我们今天要讨论的,就不再继续深究了。
我们有两个着手点,来去看登陆是如何进行的,一个是检查点击登陆按钮时, 系统是如何相应点击事件的,另一个是看点击按钮后,系统发送了什么网络请求。
我们用 inspector 查看登陆按钮,即可看到点击事件的代码位置:
接下来在点击开始时打下断点:
这样,我们输入账号密码点击登陆时,程序就会中断,这时我们要清空网络访问请求日志, 再放开断点,检查之后浏览器都发送了哪些请求:
看上去蓝色的这条请求最像是发送给服务端的登陆请求,我们可以看到我们的账号, 但是看不到我们的密码,所以应该是发送前,密码被以某种形式加密了, 我们可以点击 initiator 中的代码位置,看看源码:
这是一段「奇技淫巧」的代码,首先这个函数被定义为一个属性,
然后函数的功能是在 head 处,添加一个 <script>
的标签,
然后标签的 src
属性被设为传入参数 t,所以 t 就是请求的 url,
但是这个请求不是通过常规的 XHR 请求,而是加入一个 <script>
标签,
然后利用浏览器的特性去加载这个请求,所以如果我们打算用浏览器自带的
「Pause on any URL」的 XHR 断点,那么很抱歉,程序不会中断在这里。
我们回到火狐 network 页签,看一下登陆请求的 stacktrace:
既然 loadScript 是发送登陆请求的位置,那么我们也可以推断就是 submit
把 url
传递给 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
,这次我们要用搜索:
又是一段非常难懂的代码,但是我们离答案已经很近了,我们可以在这里下一个断点,
验证一下是不是登陆时会走到这里,我这里就略过了,答案是「是」,所以这里一定是
生成 url 的地方,2954 行的这个 for 已经超出了我的理解范围,不过还好我看到了这个:
e = (0, k['default']) ('p').value
「p」我们还记得就是密码框的 id
,我们可以在 console 下面看看
(0, k['default'])
是什么:
居然是个函数,点小箭头我们就可以看到定义:
var $ = function $(t) {
return 'string' == typeof t ? document.getElementById(t) : t
},
所以我们大概可以猜到 (0, k['default']) ('p').value
是个什么,没错,
就是我们的密码,所以这个 for
就是算出 url
所有参数的地方。
密码是如何加密的,我就不再往下继续了,看这个 for
真的很头大,
我也不怎么感兴趣,就算研究出来估计也不好发表,就到这里吧,
而且我觉得我的目标基本也已经达到了,虽然不太严谨,但是基本我们可以认定,
qq 邮箱的做法也是发送前,对用户的密码进行了某种加密。
同时我觉得也从另一方面说明,这个加密真的是跟我最早想得差不多,如果你想知道, 你一定可以知道的,某种程度上也算是防君子不防小人吧。