工欲善其事,必先利其器。git
给本身定下写文章的目标后,就去找了几家博客平台来发布文章;做为一个懒人,不能全部博客文章都手动去各家平台发布,只好经过编写脚原本发布。可是除了Github
提供了比较详细的Api
外,其余国内的博客平台都没有提供对应的接口,但总有办法的。github
下面是我对某家博客平台模拟登陆流程的记录(打死我都不会说这家平台是S开头的),我的以为挺有意思的,也能从中学到很多产品安全设计的思路。ajax
Babel
chrome
Cheerio.js
segmentfault
SuperAgent
后端
Chrome 浏览器
api
注:工具只是实现结果的一个手段,并不必定须要掌握这些工具,只要知道它们是干吗的就好了。浏览器
先进入主页找到用户登陆页,以下图所示:安全
标准的登陆框,在这边须要把Chrome
的控制台打开,进入Network
页,把 Preserve log (页面跳转也能记录日志,感谢 铁臂狗 告知)的选项勾中, 以下图所示:
抓包分析请求,先从输入正确密码开始:
我把暴露隐私的两个地方打码了(这两块也是咱们接下来要着重要分析的点)
能够从中看到请求头,咱们先把这些请求头照抄下来
const base_headers = { Accept: '*/*', 'Accept-Encoding':'gzip, deflate', 'Accept-Language':'zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4,ja;q=0.2', 'Cache-Control':'no-cache', Connection:'keep-alive', DNT:1, Host:'segmentfault.com', Origin: 'http://segmentfault.com', Pragma:'no-cache', Referer: 'http://segmentfault.com/', 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36', 'X-Requested-With': 'XMLHttpRequest' }
咱们能够看到在请求登陆的时候 Header 就已经带有 Cookie 了,这在我日常的设计中没有作过,因此我就试着把 Cookie 删后再请求,看看有什么效果。删除 Cookie 的方法以下所示:
利用排除法不停删除并继续试着登陆,都能完成登陆;直到删除 PHPSESSID 的时候发现删除以后再登录是会报错的,因此这个 PHPSESSID 确定是有用的(没用过PHP对这个不太了解),所以我判定这个 Cookie 是在后端做为验证登陆的一个字段;所以我能够经过在登陆以前先下载首页并拿到 Cookie,放到请求头上作做为模拟 Header。
import request from 'superagent' let cookie; req .get(urls.mainpage) .end((err, res) => { // 从上图能够看到咱们须要的cookie是PHPSESSID开头的 cookie = res.headers['set-cookie'] .join(',').match(/(PHPSESSID=.+?);/)[1] })
本觉得拿到 Cookie 以后就能够开开心心的作登陆请求,然而这么简单的话这篇文章页也就没什么写的必要了。
继续分析请求 HTTP 包,能够发如今每次请求的时候,url 后面老是会带一个 queryString(图 2),我在这里耗费了很多时间,毫无头绪,只能追进源码里面摸索。
找到上图中的源码,能够看到这个源码是被压缩过的,不要着急,chrome 提供了 formatt 功能,点击最下面的{}
,能够对压缩的代码重排,至少是勉强能够阅读的代码了。
接下来的事情就是怎么从这堆代码中抽丝剥茧找到对咱们有用的信息,但是这么多的代码一步步看下来也会看到头晕脑胀,眼睛滴血。那么就试试看能不能使用查找的方式从源码中找到我须要的东西。使用快捷键 ctrl+F,键入 /login
(/login
是做为登陆的连接的,感受上可能会有很大几率能搜到相关代码)
很巧的是,搜到了相关的代码。从中能够看到此网站使用了 JQuery 的 Ajax 发送相关 HTTP 请求,那么,url 即是 e.attr("action")
,从下面的 DOM 结构能看到 action 是api/user/login
。
仍是没有找到 queryString, 那就换个关键词试试看,此次搜索 _=
(看图2,queryString 是由_=
拼接起来的)
从上图能够看到有7个结果,而被黄色标注出来的那行才是咱们想要的。JQ 的 ajaxSend 能够在 Ajax 发送以前作一些处理。从上图能够看出,请求的时候在 url 的后面增长一个 n._
,那就继续去找n._是什么?因为截图截少了,我就再也不从新截图,从上图的第一行能够看到 _ 是window.SF.token,由此咱们就摸到 token 的 G 点,整个流程明朗了许多。接下来全局搜索 window.SF.token,没找到。我知道 window 是全局变量,为何把 token 放到 window 上?能够想多的是 token 并无在当前的 script 标签内。接下来去 index.html 内查找:
找到了!能够看到 token 是被包裹在一个独立的 script 标签内,在后端生成HTML模板的时候就已经插入。
找到 token 以后就很简单了,拿到这个字符串表达式,运行,拿到token。
原理我以前写过一篇文章,移步
import cheerio from 'cheerio' import request from 'superagent' let cookie; // 为何这样作 function getToken(s) { let $ = cheerio.load(s) , text = $('body script').eq(2).text() , fn = new Function('window', text + ';return window.SF.token') , token = fn({}) $ = null return token } req .get(urls.mainpage) .end((err, res) => { let token = getToken(res.text) // 从上图能够看到咱们须要的cookie是PHPSESSID开头的 cookie = res.headers['set-cookie'] .join(',') .match(/(PHPSESSID=.+?);/)[1] })
拿到 token 和 Cookie ,抓包分析所须要的登陆字段:
{ mail: 'xxxxx@xx.xx', // 邮箱 password: 'xxxxxxx', // 密码 remember: '1' // 是否记住登陆 }
登陆:
req .get(urls.mainpage) .end((err, res) => { let token = getToken(res.text) // 从上图能够看到咱们须要的cookie是PHPSESSID开头的 cookie = res.headers['set-cookie'].join(',') .match(/(PHPSESSID=.+?);/)[1] req .post(urls.login) .query({'_': token}) .set(base_headers) .set('Cookie', cookie) .type('form') .send(conf) .redirects(0) .end((err, res) => { console.log(res) }) }) })
世上无难事只怕有心人
登陆是最基础也最核心的功能,经过对登陆流程的分析,基本弄清楚了此博客平台的验证机制,在分析的过程当中斗智斗勇,利用本身掌握的知识一步一步破解谜题的自己就是一件颇有意思的事情,之后也能够将此方法用到本身的登陆流程设计中。
登陆以后能施展的手段就不少了: 提问题,发表文章,建立标签等等,用到得知识都在上面说过了,按下不表。
有须要源码的同窗,欢迎 Star