博客园的模拟登陆实现以及加密方式浅析

此文原由

有园友私信我探讨关于博客园模拟登陆的事,年前也玩了一段时间的 node(详情能够参考个人 node 项目集 https://github.com/hanzichi/funny-node 厚着脸皮求 star),作以前想的可能只是一次简单的 post,尝试下来完成后仍是有了很多收获。为了能让后人有个参考,遂成此文。php

登陆抓包

闲话少说,既然是模拟登陆,咱们来看看登陆过程都发生了什么。html

打开登陆页面(http://passport.cnblogs.com/user/signin?ReturnUrl=http://passport.cnblogs.com/),填入用户名和密码,点击登陆后,咱们很容易地抓到了登陆包。node

先看返回头:git

后续的实践中,我用回帖操做来证实已经完成登陆。抓取回帖的包,发现回帖操做须要携带一个 key 为 .CNBlogsCookie 的 cookie 识别身份。又发现,只要一次登陆后将浏览器中的该 cookie 取出,就能模拟该用户了。我将客户端的 key 为 .CNBlogsCookie 的 cookie 取出,写下以下代码:github

superagent
  .post('http://www.cnblogs.com/mvc/PostComment/Add.aspx')
  .set("Cookie", ".CNBlogsCookie=yourCookieValue")
  .send({"blogApp": "xxx"})
  .send({"body": "test"})
  .send({"postId": xxx})
  .end(function (err, sres) { // callback
  });

竟然能回帖,彻底不用管其余操做了。不明缘由,可是细思极恐,若是你被人盗取了该 cookie,后果你懂的。ajax

要手动从浏览器中复制获取该 cookie 显得有点 low,如何能自动获取该 cookie 值?算法

再看请求头:浏览器

实践发现,有四个值是必须模拟的(已标出),并且全都照抄便可。安全

最后看 post 的数据:cookie

这是什么玩意?原来是加密后的用户名以及密码数据。接下去简单了解下加密方式(尽管模拟登陆并不强依赖于此)。

jsencrypt

博客园的加密方式为 RSA,依赖 jsencrypt 这个库。

这里不详述 RSA 加密方式,详情能够参考阮一峰老师的文章:

jsencrypt 加密是可逆的加密方式,客户端用公钥加密,服务端用私钥解密,每次加密生成的字符串都不同,可是解密后都同样。用明文发送帐户名和密码,若是该包被截获,那么你的密码也将大白于天下,存在着极大的安全隐患,可是客户端用了 jsencrypt 加密,若是被截获,截获的也仅仅只是加密后的字符串,没有私钥解密的话,也无济于事。咱们 post 包中的 input1 和 input2 的数据正是在客户端加密后的用户名以及密码。

打开 http://passport.cnblogs.com/user/signin?ReturnUrl=http://passport.cnblogs.com,ctrl+u 查看网页源代码,能够清楚看到 jsencrypt 加密的公钥,帐户名密码的加密过程,以及用 ajax 方式登陆所须要的数据等。

参考 博客园加密登陆--jsencrypt 我写了个简单的基于 jsencrypt 的加密解密 demo https://github.com/hanzichi/funny-node/tree/master/cnblogs-auto-login/jsencrypt-demo。由于解密过程有用到 PHP 中的 openssl,因此记得在 php.ini 文件中打开 openssl,具体操做为找到 extension=php_openssl.dll 这一行,把注释打开(将前面的封号去掉)。

另外,根据已经披露的文献,目前被破解的最长 RSA 密钥是 768 个二进制位。也就是说,长度超过 768 位的密钥,还没法破解(至少没人公开宣布)。所以能够认为,1024 位的 RSA 密钥基本安全,2048 位的密钥极其安全。Online RSA Key Generator 能够参考 http://travistidwell.com/jsencrypt/demo/index.html,我这也备份了一份 https://github.com/hanzichi/funny-node/tree/master/cnblogs-auto-login/key-generator

编码

进入最后编码阶段。

首先咱们须要获取加密后的帐户名以及密码,能够抓个包复制下参数,虽然每次加密后的字符串都不同,可是解密后的结果是同样的,因此若是后期不主动修改用户名和密码,这样作彻底没有问题。可是我以为这样作不优雅,能不能经过用户名和密码,获取加密后的结果?尝试着找了下 node 下的 RSA 模块,无奈可能使用方式不大同样,未果。因而换了个方式,客户端进行加密,将加密后的数据存储到服务端,供 node 调用。

而后就是模拟登陆了,VerificationToken 参数能够爬取页面取,也能够直接拿个值赋值。登陆成功后保存 cookie 以便下次操做时调用。

详细代码和操做步骤已同步在 Github,欢迎交流探讨。