微信二维码登录中的JS代码分析

在很多地方就是都出现了使用二维码登录,二维码付款,二维码账户等应用(这里的二维码种马,诈骗就不说了),二维码验证,多终端辅助授权应用开始多起来,这里先说下啥是二维码,其实二维码就是存了二进制数据的黑白图片,当出现要求二维码登录的时候,服务器会生成一条临时的唯一的二维码信息,发送到客户端以二维码(图片)的形式写入到网页,然后你就会看到统一的四个方形的二维码,如果做的好这个二维码信息应该是有时效的,这里暂且不考虑这些,就简单的微信登录作为例子看看吧,

首先说下整个授权流程:

在客户端网页中会不断向服务器发送https连接,并且这里传输很少的数据之后就断开连接了,下面看下微信网页中这个login1c709c.js文件:

(function($, _aoWin) {
_aoWin.QRLogin = {};
  _aoWin.LoginLog = ;
 var _sBaseHost = ,
    _oLoginQrCodeImg = document.getElementById(loginQrCode);
 if (document.domain == qq.com) {
 _sBaseHost = weixin.qq.com;
 } else if(location.hostname.match(/(wechat\.com)$/)){
 _sBaseHost = wechat.com;
 }else{
    _sBaseHost = wechatapp.com;
  }
 
 var show_tip = 1,
 _sCurUUId,
 _oResetTimeout,
    _aWebMMCallbacks = [],
    _oDetactWebMMInterval = setInterval(function(){
      if(_aoWin.WebMM){
        clearInterval(_oDetactWebMMInterval);
        var callback;
        while(callback = _aWebMMCallbacks.shift()){
          if(typeof(callback) != function) continue;
          callback();
        }
      }
    }, 1000);

function _logInPage(_asLog){
    _aoWin.LoginLog = LoginLog + _asLog + \n;
  }
 
  function _afterLoadWebMMDo(callback){
    if(!_aoWin.WebMM){
      _aWebMMCallbacks.push(callback);
    }else{
      callback();
    }
  }
 
  function _reportNow(text){
    _logInPage(text);
    _afterLoadWebMMDo(function(){
      WebMM.ossLog({Text: text});
      WebMM.flushOssLog();
    });
  }
 
  var reLoadQRImgCount = 0,
    loadQRCodeTime = 0,
    loadQRImgSucc = function(){
      clearInterval(loadQRImgWatchDog);
      _logInPage(Load QRCode Success, time= + (new Date().getTime() - loadQRCodeTime) + ms, reload count:  + reLoadQRImgCount);
    },
    loadQRImgFail = function(img){
      _reportNow(Load QRcode fail! + status + , src:  + img.src + , time:  + (new Date().getTime() - loadQRCodeTime) + ms);
    },
    loadQRImgWatchDog = null;
 function _loadQRImg(uuid) {
    _poll(uuid);
    _logInPage(Load QRCode Start);
    loadQRCodeTime = new Date().getTime();
 
    _oLoginQrCodeImg.onload = function(){
      loadQRImgSucc();
      _oLoginQrCodeImg.onload = null;
    };
    _oLoginQrCodeImg.onerror = function(){loadQRImgFail(this)};
    _oLoginQrCodeImg.src = https://login.+_sBaseHost+/qrcode/+uuid+?t=webwx;
 
    loadQRImgWatchDog = setInterval(function(){
      if (reLoadQRImgCount >= 5) {
        _reset();
        return;
      }
      reLoadQRImgCount++;
 
      var _img = new Image();
      _img.onload = function () {
        if(!_oLoginQrCodeImg.onload) return;
 
        _oLoginQrCodeImg.onload = null;
        _oLoginQrCodeImg.src = this.src;//replace
        loadQRImgSucc();
      };
      _img.onerror = function(){loadQRImgFail(this)};
      _img.src = _oLoginQrCodeImg.src + &r= + new Date().getTime();
    }, 5000);
  }
 
  var _sSecondRequestTime = 0,
    _nAjaxTimeout = 100 * 1000,
    _nNewLoginFuncErrCount = 0;
 function _poll(_asUUID) {
 var _self = arguments.callee,
      _nTime = 0;
 _sCurUUId = _asUUID;
 
    _logInPage(_poll Request Start, time:  + new Date().getTime());
    _nTime = new Date().getTime();
 $.ajax({
 type: GET,
 url: https://login. + _sBaseHost + /cgi-bin/mmwebwx-bin/login?uuid= + _asUUID + &tip= + show_tip,
 dataType: script,
 cache: false,
 timeout: _nAjaxTimeout,
 success: function(data, textStatus, jqXHR) {
      _logInPage(_poll Request Success, code:  + window.code + , time:  + (new Date().getTime() - _nTime) + ms);
  switch (_aoWin.code) {
  case 200:
   _sSecondRequestTime = new Date().getTime() - _sSecondRequestTime;
        _logInPage(Second Request Success, time:  + _sSecondRequestTime + ms);
  clearTimeout(_oResetTimeout);
 
        var _fNewLoginFunc = function(){
          $.ajax({
            url: _aoWin.redirect_uri + &fun=new,//new login page
            type: GET,
            success:function(msg) {
              _logInPage(new func reponse, reponseMsg:  + msg);
              var code = msg.match(/<script>(.*)<\/script>/);
              var skey=msg.match(/<skey>(.*)<\/skey>/);
              if(code){
                eval(code[1]);
              }else{
                $(#container).show();
                $(#login_container).hide();
              }
              if(skey && skey[1]){
               WebMM.model(account).setSkey(skey[1]);
              }
            },
            error:function(jqXHR, textStatus, errorThrown){
              _nNewLoginFuncErrCount++;
              if(_nNewLoginFuncErrCount > 5){
                if(confirm(Call new login page func error, refresh?)){location.reload()}
                return;
              }
              _reportNow(_aoWin.redirect_uri +  New login page func error:  + textStatus + retryCount: + _nNewLoginFuncErrCount);
              setTimeout(_fNewLoginFunc, 500);
            }
          });
        };
        _fNewLoginFunc();
 
        _reportNow(/cgi-bin/mmwebwx-bin/login, Second Request Success, uuid:  + _asUUID + , time:  + _sSecondRequestTime + ms);
  break;
 
  case 201:
        clearTimeout(_oResetTimeout);
  show_tip = 0;
  $('.errorMsg').hide();
  $('.normlDesc').hide();
  $('.successMsg').show();
        _reportNow(/cgi-bin/mmwebwx-bin/login, First Request Success, uuid:  + _asUUID);
        _reportNow(/cgi-bin/mmwebwx-bin/login, Second Request Start, uuid:  + _asUUID);
 
        _sSecondRequestTime = new Date().getTime();
 
        //_nAjaxTimeout = 5 * 1000;
        _self(_asUUID);
        break;
 
  case 408:
  setTimeout(function(){
   _self(_asUUID);
  }, 500);
  break;
 
  case 400:
  case 500:
        _reset();
        _afterLoadWebMMDo(function(){
   _aoWin.Log.d(500, Login Poll Svr Exception);
  });
  break;
  }
 },
 error: function(jqXHR, textStatus, errorThrown) {
  if (textStatus == 'timeout') {
        setTimeout(function(){
          _self(_asUUID);
        }, 500);
  } else {
        setTimeout(function(){
          _self(_asUUID);
        }, 5000);
 
        _logInPage(_poll Request Error: + textStatus);
        _afterLoadWebMMDo(function(){
          _aoWin.Log.e(Login Poll Error: + textStatus);
        });
  }
 }
 });
 }

var getUUIDCount = 0,
    _getUUIDWatchDog,
    _bGetUUIDSuccess = false;//ajax successִ
 function _getUUID() {
    getUUIDCount++;
    var _self = arguments.callee,
      _loadError = function(errorText){
        _reportNow(Load UUID Error! ErrorText:  + errorText +  getUUIDCount= + getUUIDCount);
        if(getUUIDCount > 5){
          if (confirm(Load uuid error. Refresh?)) {
            location.reload();
          }
        }
        setTimeout(function(){
          _self();
        }, 500);
      };
 
    clearTimeout(_getUUIDWatchDog);
    _getUUIDWatchDog = setTimeout(function(){
      if(!_aoWin.QRLogin.code){
        _logInPage(GetUUID Timeout, WatchDog Run);
        _self();
      }
    }, 10000);
    
        $.ajax({      
          type: GET,      
          url: https://login. + _sBaseHost + /jslogin?appid=wx782c26e4c19acffb&redirect_uri=+encodeURIComponent(location.protocol+//+location.host+/cgi-bin/mmwebwx-bin/webwxnewloginpage)+&fun=new&lang= + document.lang,
           dataType: script,
      cache: false,
      success : function(){
        clearTimeout(_getUUIDWatchDog);
        if(_bGetUUIDSuccess) return;
        if (_aoWin.QRLogin && _aoWin.QRLogin.code == 200) {
          _logInPage(GetUUID Success, UUID= + QRLogin.uuid);
          _bGetUUIDSuccess = true;
 
          clearTimeout(_oResetTimeout);
          _oResetTimeout = setTimeout(function(){
            location.reload();//Note: Don't run _reset(). If you run _reset(), there will may have many _poll request, as they get 408 return code
          }, 5 * 60 *1000);//5 mins
 
          _loadQRImg(QRLogin.uuid);
        } else {
          var QRLoginCode = (_aoWin.QRLogin && _aoWin.QRLogin.code) ? _aoWin.QRLogin.code : None;
          _logInPage(GetUUID Error, QRLogin.code= + QRLoginCode);
          _loadError(QRLogin.code=  + QRLoginCode);
        }
      },
      error : function(xhr, textStatus, errorThrown){
        _logInPage(GetUUID Error, textStatus= + textStatus);
        _loadError(textStatus);
      }
    });
 }
 
  function _reset(){
    location.reload();
  }
 
 if ($(#login_container).is(:visible) ) {
    _getUUID();
 }
 
  
 var _bHadLog = false;
 function _ossLog() {
 if (_bHadLog) return;
 _bHadLog = true;
 var _sUvid = document.cookie.match(new RegExp( (^| )+webwxuvid+=([^;]*)(;|$)));
    if(!_sUvid || _sUvid.length < 3) return;
    _sUvid = _sUvid[2];
 (new Image()).src = /cgi-bin/mmwebwx-bin/webwxstatreport?funkey=indexdemo&uvid=+_sUvid+&uuid=+_sCurUUId;
 }
 
 
 if($(img.guide).length > 0) {
 var _nTimer = 0,
  _oGuide$ = $(.guide),
  _oGuideTrigger$ = $(#guideTrigger, #tipTrigger),
  _oMask$ = $(.mask);
 
  function _back() {
  _nTimer = setTimeout(function() {
  _oMask$.stop().animate({opacity:0}, function(){$(.mask).hide()});
  _oGuide$.stop().animate({marginLeft:-120px,opacity:0}, 400, swing,function(){
   _oGuide$.hide();
  });
  }, 100);
 }
 
 /*guide*/
 _oGuide$.css({left:50%, opacity:0});
 _oGuideTrigger$.css({backgroundColor:white, opacity:0});
 _oGuideTrigger$.mouseover(function(){
  clearTimeout(_nTimer);
  _oMask$.show().stop().animate({opacity:0.2});
  _oGuide$.css(display, block).stop().animate({marginLeft:+168px, opacity:1}, 900, swing, function() {
  _oGuide$.animate({marginLeft:+153px}, 300);
  });
  _ossLog();
 }).mouseout(_back);
 
 _oGuide$.mouseover(function(){
  clearTimeout(_nTimer);
 }).mouseout(_back);
 }
})(jQuery, window);

细读js之后,你就会从网页客户端这边看到请求登录的一面,网页客户端每隔500毫秒就向服务器发起ssl请求,请求当前的二维码是否被其他客户端(手机)授权,如果返回结果是201,就是说明已经获取扫描二维码终端相同的账号登录授权,如果是其他情况就再隔500毫秒再循环发请求。这个过程会一直持续到二维码被扫描通过或者二维码超时(失效)为止。
其中使用的工具有: 抓包工具 Fidller ,Chrome F12开发人员工具,注意偶然的发现,微信的客户端有一个min-webmm1cba21.js ,其中清晰可见的XSS filter规范。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


网页授权获取用户信息的方法
报错config:invalid signature的解决方案
微信开发百思不得姐实战教程
详解微信开发input输入框
教你libco是如何支撑巨大数据信息量的
微信二次开发之文本消息请求与发送
微信开发H5轻游戏
scroll-view完成列表页的方法详解
Java微信开发之自定义菜单的创建
微信开发之input控件的实例详解
微信开发Emoji表情的实例教程
微信开发中详解textarea的使用方法
微信开发中使元素占满全屏的方法介绍
微信开发之数据访问的方法详解
微信二次开发之各类型消息封装
微信开发之数据库操作
如何获取微信好友的地理位置信息
分享3款微信开发开源框架
微信开发之获取服务器IP
微信开发之公交换乘功能代码详解