假前,寢室的猴哥跟我說幫忙寫個新浪愛問的自動簽到軟件。結果一回來就感冒了,而且還是跨年的感冒。。。這中間什么都不想做,天天圍在火旁邊,吃東西、看電視。廢話不多說了,開始正題。
對於新浪的網站,只要你登錄了其中一個,通過Cookie驗證機制,你也就自動登錄了其他網站。在試驗登錄新浪愛問的過程中,發現如果異地登錄的話,會跳轉到新浪會員登錄頁面。因此,本文分析了新浪會員登錄頁面的登錄過程。
分析過程中使用的工具:Mozilla Firefox瀏覽器以及相應插件FireBug、HttpFox、HttpRequester
頁面中登錄窗口如下圖所示:
整個登錄過程中的瀏覽器與服務器端的通信如下:
1.輸入完登錄名后,輸入焦點離開登錄名輸入框后,瀏覽器會向服務器發送請求:http://login.sina.com.cn/bindmail/checkmailuser.php?_r=1328093414030,請求方式為POST,POST的數據:name=pzxbc%40qq.com&type=json&ag=;返回數據:{"ret":"n","errcode":"limited"}
POST數據解釋:name:賬戶名,使用JavaScript函數encodeURIComponent進行URI編碼。對應Python中相關函數urllib.quote();type:json;ag:空值
返回數據解釋:Request函數根據返回的值進行相關處理,具體請參看下面的源代碼 。
產生請求的原因: 在加載signin.php文件過程中執行了一下JS腳本,給登錄名的輸入框添加了一個Onblur事件函數。在輸入焦點離開輸入框后,調用了Onblur事件響應函數,Onblur函數調用了Request()函數,Request()函數通過調用ajaxCheck()產生上述請求。
bind(nameInput,"blur",function(){
nameTip.className = 'red_ero';
if(/^\s*$/.test(nameInput.value)) {
close('newDiv');
return;
}
if(/^\s*\d{1,9}\s*$/.test(nameInput.value)) {
//if (!submitFlag || $('ag').value == '')
openNewDiv('newDiv');
} else {
close('newDiv');
request();
}
});
function request(){
var ag = $('ag').value;
var url = "http://login.sina.com.cn/bindmail/checkmailuser.php";
ajaxCheck(location.protocol == "https:" ? url.replace(/^http:/, "https:") : url,"name="+encodeURIComponent(nameInput.value)+"&type=json&ag="+ag,function(res){
var data = eval('('+res+')');
if(data.ret == "y") {
nameTip.className = 'red_ero red_ero_fix';
nameTip.innerHTML = '<span class="yes"></span>為了您的帳號安全,請<a href="/bindmail/signin.php?username=' +nameInput.value+ '">綁定郵箱</a>';
} else if(data.ret == "n" && data.mail) {
nameTip.className = 'red_ero red_ero_fix';
if (data.errcode == 'not_verify') {
nameTip.innerHTML = '<span class="yes"></span>您的郵箱: '+data.mail+' 未驗證,點此<a href="/bindmail/bindmail1.php?entry=sso&user='+data.mail+'">重發驗證郵件</a>';
} else {
nameTip.innerHTML = '<span class="yes"></span>用您的郵箱'+data.mail+'也可以登錄';
}
}
});
function ajaxCheck(url,data, callBack) {
var XHR;
var date = new Date();
try {
try{
XHR=new ActiveXObject("Microsoft.XMLHTTP");
}catch(e){
try{
XHR=new XMLHttpRequest();
} catch (e){ }
}
XHR.open("POST",url+"?_r="+date.getTime());
XHR.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
XHR.onreadystatechange = function(){
if(XHR.readyState==4) {
if(XHR.status==200) {
if(callBack) callBack(XHR.responseText);
}
}
}
XHR.send(data);
}catch (e) {
alert(e.message);
}
}
2.點擊登錄按鈕后,產生請求:https://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.3.17),POST數據:entry=account&gateway=1&from=&savestate=0&useticket=0&vsnf=1&su=cHp4YmMlNDBxcS5jb20%3D&service=account&sp=yourpassword&encoding=UTF-8&callback=parent.sinaSSOController.loginCallBack&returntype=IFRAME&setdomain=1;返回的數據:
<script type="text/javascript" language="javascript">
location.replace("http://login.sina.com.cn/crossdomain2.php?action=logincallback&retcode=4049&reason=%D3%C9%D3%DA%C4%FA%B5%C7%C2%BC%D2%EC%B3%A3%A3%AC%C7%EB%CC%EE%D0%B4%D1%E9%D6%A4%C2%EB&callback=parent.sinaSSOController.loginCallBack&setdomain=1");
</script>
POST數據解釋:su:帳號名,先通過JavaScript的encodeURIComponen()函數編碼,然后再進行base64編碼;sp:你的密碼。
產生請求的原因:signin.php文件中的OnReady()函數添加了表單的OnSumbit處理函數
$("vForm").onsubmit = function(){return login();};
login()調用sinaSSOController.login()->sinaSSOController.loginMethodCheck()->sinaSSOController.loginByConfig() ->sinaSSOController.loginByIframe(),在loginByIframe()函數中新建了一個隱藏的loginForm,通過表單的Submit方法產生上述請求。
3.步驟2請求后返回的內容,會是瀏覽器重定向到返回數據中所指示的網址:http://login.sina.com.cn/crossdomain2.php?action=logincallback&retcode=4049&reason=%D3%C9%D3%DA%C4%FA%B5%C7%C2%BC%D2%EC%B3%A3%A3%AC%C7%EB%CC%EE%D0%B4%D1%E9%D6%A4%C2%EB&callback=parent.sinaSSOController.loginCallBack&setdomain=1,所以會產生一個GET請求。如果返回數據中retcode值為0,表示登錄成功。
<script>
if(1) document.domain = "sina.com.cn";
parent.sinaSSOController.setCrossDomainUrlList({"retcode":0,"arrURL":["http:\/\/weibo.com\/sso\/crosdom?action=login","http:\/\/kandian.com\/logon\/do_crossdomain.php?action=login","http:\/\/login.t.cn\/sinaurl\/sso.json?action=login&uid=1830667973"]});parent.sinaSSOController.loginCallBack({"retcode":"0","uid":"1830667973"});;
</script>
返回以下兩種內容表示,登錄不成功
<script>
//情況1:異地登錄,需要重新輸入驗證碼
if(1) document.domain = "sina.com.cn";
parent.sinaSSOController.loginCallBack({"retcode":"4049","reason":"\u7531\u4e8e\u60a8\u767b\u5f55\u5f02\u5e38\uff0c\u8bf7\u586b\u5199\u9a8c\u8bc1\u7801"});;
</script>
<script>
//情況2:驗證碼輸入錯誤
if(1) document.domain = "sina.com.cn";
parent.sinaSSOController.loginCallBack({"retcode":"2070","reason":"\u8f93\u5165\u7684\u9a8c\u8bc1\u7801\u4e0d\u6b63\u786e"});;
</script>
4.然后交叉登錄新浪的其他網站,分別請求一下三個地址:
http://weibo.com/sso/crosdom?action=login&callback=sinaSSOController.doCrossDomainCallBack&scriptId=ssoscript0&client=ssologin.js(v1.3.17)&_=1328256974181;
http://kandian.com/logon/do_crossdomain.php?action=login&callback=sinaSSOController.doCrossDomainCallBack&scriptId=ssoscript1&client=ssologin.js(v1.3.17)&_=1328256974192;
http://login.t.cn/sinaurl/sso.json?action=login&uid=1830667973&callback=sinaSSOController.doCrossDomainCallBack&scriptId=ssoscript2&client=ssologin.js(v1.3.17)&_=1328256974211
其中_=后面的是JavaScript中time()函數的返回值,uid為步驟3中的返回值。
5.登錄成功后,調用signinconfig.js中的customLoginCallBack()函數,customLoginCallBack()函數產生GET請求:http://login.sina.com.cn/,然后轉向:http://login.sina.com.cn/member/app.php?entry=sso&act=my,最后轉向:http://login.sina.com.cn/member/my.php?entry=sso,至此整個登錄過程完成了。
var url = getReturnUrl(); //返回URL:http://login.sina.com.cn/
if (this.crossDomain) {
window.location.replace(url);
return true;