在Python中用Request库模拟登录(三):Discuz论坛(未加密,有验证码,有隐藏验证)
以Discuz的官方站为例。直接点击网页右上角的登录按钮,会弹出一个带验证码的登录窗口。输入验证码之后,会检查验证码是否正确。然后登录。首先,通过抓包分析,这些过程浏览器和服务器交换了哪些数据。
抓包分析
整个过程产生了5条数据:
一
第一个是GET请求,返回了一段html代码
<div id="main_messaqge_LZH8S"> <div id="layer_login_LZH8S"> <h3 class="flb"> <em id="returnmessage_LZH8S"> 用户登录</em> <span><a href="javascript:;" class="flbc" onclick="hideWindow('login', 0, 1);" title="关闭">关闭</a></span> </h3> <form method="post" autocomplete="off" name="login" id="loginform_LZH8S" class="cl" onsubmit="pwdclear = 1;ajaxpost('loginform_LZH8S', 'returnmessage_LZH8S', 'returnmessage_LZH8S', 'onerror');return false;" action="member.php?mod=logging&action=login&loginsubmit=yes&handlekey=login&loginhash=LZH8S"> <div class="c cl"> <input type="hidden" name="formhash" value="41969484" /> <input type="hidden" name="referer" value="http://www.discuz.net/forum.php" /> <div class="rfm"> <table> <tr> <th> <span class="login_slct"> <select name="loginfield" style="float: left;" width="45" id="loginfield_LZH8S"> <option value="username">用户名</option> <option value="email">Email</option> </select> </span> </th> <td><input type="text" name="username" id="username_LZH8S" autocomplete="off" size="30" class="px p_fre" tabindex="1" value="" /></td> <td class="tipcol"><a href="member.php?mod=register">立即注册</a></td> </tr> </table> </div> <div class="rfm"> <table> <tr> <th><label for="password3_LZH8S">密码:</label></th> <td><input type="password" id="password3_LZH8S" name="password" onfocus="clearpwd()" size="30" class="px p_fre" tabindex="1" /></td> <td class="tipcol"><a href="javascript:;" onclick="display('layer_login_LZH8S');display('layer_lostpw_LZH8S');" title="找回密码">找回密码</a></td> </tr> </table> </div> <div class="rfm"> <table> <tr> <th>安全提问:</th> <td><select id="loginquestionid_LZH8S" width="213" name="questionid" onchange="if($('loginquestionid_LZH8S').value > 0) {$('loginanswer_row_LZH8S').style.display='';} else {$('loginanswer_row_LZH8S').style.display='none';}"> <option value="0">安全提问(未设置请忽略)</option> <option value="1">母亲的名字</option> <option value="2">爷爷的名字</option> <option value="3">父亲出生的城市</option> <option value="4">您其中一位老师的名字</option> <option value="5">您个人计算机的型号</option> <option value="6">您最喜欢的餐馆名称</option> <option value="7">驾驶执照最后四位数字</option> </select></td> </tr> </table> </div> <div class="rfm" id="loginanswer_row_LZH8S" style="display:none"> <table> <tr> <th>答案:</th> <td><input type="text" name="answer" id="loginanswer_LZH8S" autocomplete="off" size="30" class="px p_fre" tabindex="1" /></td> </tr> </table> </div> <span id="seccode_cSA"></span> <script type="text/javascript" reload="1">updateseccode('cSA', '<div class="rfm"><table><tr><th><sec>: </th><td><sec><br /><sec></td></tr></table></div>', 'member::logging');</script> <div class="rfm bw0"> <table> <tr> <th></th> <td><label for="cookietime_LZH8S"><input type="checkbox" class="pc" name="cookietime" id="cookietime_LZH8S" tabindex="1" value="2592000" />自动登录</label></td> </tr> </table> </div> <div class="rfm mbw bw0"> <table width="100%"> <tr> <th> </th> <td> <button class="pn pnc" type="submit" name="loginsubmit" value="true" tabindex="1"><strong>登录</strong></button> </td> <td> </td> </tr> </table> </div> <div class="rfm bw0 "> <hr class="l" /> <table> <tr> <th>快捷登录:</th> <td> <a href="http://www.discuz.net/connect.php?mod=login&op=init&referer=http%3A%2F%2Fwww.discuz.net%2Fforum.php&statfrom=login" target="_top" rel="nofollow"><img src="static/image/common/qq_login.gif" class="vm" /></a> <a href="plugin.php?id=wechat:login"><img src="source/plugin/wechat/image/wechat_login.png" class="vm" /></a> </td> </tr> </table> </div> </div> </form> </div> <div id="layer_lostpw_LZH8S" style="display: none;"> <h3 class="flb"> <em id="returnmessage3_LZH8S">找回密码</em> <span><a href="javascript:;" class="flbc" onclick="hideWindow('login')" title="关闭">关闭</a></span> </h3> <form method="post" autocomplete="off" id="lostpwform_LZH8S" class="cl" onsubmit="ajaxpost('lostpwform_LZH8S', 'returnmessage3_LZH8S', 'returnmessage3_LZH8S', 'onerror');return false;" action="member.php?mod=lostpasswd&lostpwsubmit=yes&infloat=yes"> <div class="c cl"> <input type="hidden" name="formhash" value="41969484" /> <input type="hidden" name="handlekey" value="lostpwform" /> <div class="rfm"> <table> <tr> <th><span class="rq">*</span><label for="lostpw_email">Email:</label></th> <td><input type="text" name="email" id="lostpw_email" size="30" value="" tabindex="1" class="px p_fre" /></td> </tr> </table> </div> <div class="rfm"> <table> <tr> <th><label for="lostpw_username">用户名:</label></th> <td><input type="text" name="username" id="lostpw_username" size="30" value="" tabindex="1" class="px p_fre" /></td> </tr> </table> </div> <div class="rfm mbw bw0"> <table> <tr> <th></th> <td><button class="pn pnc" type="submit" name="lostpwsubmit" value="true" tabindex="100"><span>提交</span></button></td> </tr> </table> </div> </div> </form> </div> </div> <div id="layer_message_LZH8S" style="display: none;"> <h3 class="flb" id="layer_header_LZH8S"> <em>用户登录</em> <span><a href="javascript:;" class="flbc" onclick="hideWindow('login')" title="关闭">关闭</a></span> </h3> <div class="c"><div class="alert_right"> <div id="messageleft_LZH8S"></div> <p class="alert_btnleft" id="messageright_LZH8S"></p> </div> </div> <script type="text/javascript" reload="1"> var pwdclear = 0; function initinput_login() { document.body.focus(); if($('loginform_LZH8S')) { $('loginform_LZH8S').username.focus(); } simulateSelect('loginfield_LZH8S'); } initinput_login(); function clearpwd() { if(pwdclear) { $('password3_LZH8S').value = ''; } pwdclear = 0; } </script>
登录窗口
看起来就是登录窗口。
特别注意到,这段代码中有
loginhash=LZH8S <input type="hidden" name="formhash" value="41969484" />
'LZH8S'在代码中频繁出现,formhash出现两次。
刷新之后,loginhash和formhash发生了变化。
二
第二个GET请求返回了一段JavaScript代码
if($('seccode_cSA')) { if(!$('vseccode_cSA')) { var sectpl = seccheck_tpl['cSA'] != '' ? seccheck_tpl['cSA'].replace(/<hash>/g, 'codecSA') : ''; var sectplcode = sectpl != '' ? sectpl.split('<sec>') : Array('<br />',': ','<br />',''); var string = '<input name="seccodehash" type="hidden" value="cSA" /><input name="seccodemodid" type="hidden" value="member::logging" />' + sectplcode[0] + '验证码' + sectplcode[1] + '<input name="seccodeverify" id="seccodeverify_cSA" type="text" autocomplete="off" style="ime-mode:disabled;width:100px" class="txt px vm" onblur="checksec(\'code\', \'cSA\', 0, null, \'member::logging\')" />' + ' <a href="javascript:;" onclick="updateseccode(\'cSA\');doane(event);" class="xi2">换一个</a>' + '<span id="checkseccodeverify_cSA"><img src="' + STATICURL + 'image/common/none.gif" width="16" height="16" class="vm" /></span>' + sectplcode[2] + '<span id="vseccode_cSA">请输入下面动画图片中的字符<br /><img onclick="updateseccode(\'cSA\')" width="100" height="35" src="misc.php?mod=seccode&update=18644&idhash=cSA" class="vm" alt="" /></span>' + sectplcode[3]; evalscript(string); $('seccode_cSA').innerHTML = string; } else { var string = '请输入下面动画图片中的字符<br /><img onclick="updateseccode(\'cSA\')" width="100" height="35" src="misc.php?mod=seccode&update=18644&idhash=cSA" class="vm" alt="" />'; evalscript(string); $('vseccode_cSA').innerHTML = string; } }
code info
其中的update值在刷新和点击换一个验证码之后产生变化
src="misc.php?mod=seccode&update=18644&idhash=cSA"
三
第三个GET请求的头文件包含
GET http://www.discuz.net/misc.php?mod=seccode&update=18644&idhash=cSA HTTP/1.1 Accept: image/webp,image/*,*/*;q=0.8
根据Accept属性可知返回的是一张图片,请求链接中包含了上一条请求的update值。
四
第4个GET请求的链接为
GET http://www.discuz.net/misc.php?mod=seccode&action=check&inajax=1&modid=member::logging&idhash=cSA&secverify=e38p
最后的secverify=e38p为验证码,返回的文本为
<?xml version="1.0" encoding="gbk"?> <root><![CDATA[succeed]]></root>
根据链接中的action=check和返回的文本推断,这是在检查输入的验证码是否正确。
五
第5个为POST请求,headers为
POST http://www.discuz.net/member.php?mod=logging&action=login&loginsubmit=yes&handlekey=login&loginhash=LZH8S&inajax=1 HTTP/1.1 Host: www.discuz.net Connection: keep-alive Content-Length: 200 Cache-Control: max-age=0 Origin: http://www.discuz.net Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Content-Type: application/x-www-form-urlencoded Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Referer: http://www.discuz.net/forum.php Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.8 Cookie: t7asq_4ad6_saltkey=X3kj05VV; t7asq_4ad6_lastvisit=1496539605; t7asq_4ad6_nofavfid=1; t7asq_4ad6_ulastactivity=1496722517%7C0; t7asq_4ad6_lastcheckfeed=3051978%7C1496722517; t7asq_4ad6_security_cookiereport=798bNiNKvpmS%2BJb6aMC1CK0u2rBbgm%2Bl4RHOagb%2FADNc1uz1WEc6; t7asq_4ad6_connect_is_bind=0; t7asq_4ad6_forum_lastvisit=D_10_1496722811; pgv_pvi=7116158144; pgv_info=ssi=s5527714008; t7asq_4ad6_seccode=14415.a12e22e835ed3df84a; t7asq_4ad6_lastact=1496730222%09misc.php%09seccode formhash=41969484&referer=http%3A%2F%2Fwww.discuz.net%2Fforum.php&loginfield=username&username=123&password=123&questionid=0&answer=&seccodehash=cSA&seccodemodid=member%3A%3Alogging&seccodeverify=e38p
请求的链接中含有loginhash=LZH8S,提交的数据中含有formhash=41969484和验证码seccodeverify=e38p和没有被加密的用户名和密码。
因为用户名和密码是随便输入的,所以返回的文本为
<?xml version="1.0" encoding="gbk"?> <root><![CDATA[登录失败,您还可以尝试 4 次<script type="text/javascript" reload="1">if(typeof errorhandle_login=='function') {errorhandle_login('登录失败,您还可以尝试 4 次', {'loginperm':'4'});}</script>]]></root>
下面进行模拟登录,注意要获取loginhash、formhash和update的值。
模拟登录
登录过程中的5个请求的headers都不同,在模拟请求时,完全复制抓包到的headers。
定义函数:
import requests session=requests.session() #获取登录窗口中的loginhash和formhash def get_login_window(): url='http://www.discuz.net/member.php?mod=logging&action=login&infloat=yes&handlekey=login&inajax=1&ajaxtarget=fwin_content_login' headers={'Host':'www.discuz.net','Connection':'keep-alive','User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36','X-Requested-With':'XMLHttpRequest','Accept':'*/*','Referer':'http://www.discuz.net/forum.php','Accept-Encoding':'gzip, deflate, sdch','Accept-Language':'zh-CN,zh;q=0.8'} #清空原来的headers session.headers.clear() #更新headers session.headers.update(headers) r=session.get(url) #获取loginhash p=r.text.find('loginhash')+len('loginhash')+1 loginhash=r.text[p:p+5] #获取formhash p=r.text.find('formhash')+len('formhash" value="') formhash=r.text[p:p+8] return (loginhash,formhash) #获取update def get_code_info(): url='http://www.discuz.net/misc.php?mod=seccode&action=update&idhash=cSA&0.3916181418197131&modid=member::logging' r=session.get(url) p=r.text.find('update=') update=r.text[p+7:p+12] return update #获取验证码 def get_code(update): url='http://www.discuz.net/misc.php?mod=seccode&update='+update+'&idhash=cSA' headers={'Host':'www.discuz.net','Connection':'keep-alive','User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36','Accept':'image/webp,image/*,*/*;q=0.8','Referer':'http://www.discuz.net/forum.php','Accept-Encoding':'gzip, deflate, sdch','Accept-Language':'zh-CN,zh;q=0.8'} session.headers.clear() session.headers.update(headers) r=session.get(url) if(r.content[:3]==b'GIF'): #保存验证码图片 file=open('code.gif','wb') file.write(r.content) file.close() else: #打印错误信息 print(r.text) #检查验证码是否正确 #通过人工识别验证码code,:) def check_code(code): url='http://www.discuz.net/misc.php?mod=seccode&action=check&inajax=1&modid=member::logging&idhash=cSA&secverify='+code headers={'Host':'www.discuz.net','Connection':'keep-alive','User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36','Accept':'image/webp,image/*,*/*;q=0.8','Referer':'http://www.discuz.net/forum.php','Accept-Encoding':'gzip, deflate, sdch','Accept-Language':'zh-CN,zh;q=0.8'} session.headers.clear() session.headers.update(headers) r=session.get(url) return r.text #模拟登录 def login(loginhash,formhash,code,username,password): url='http://www.discuz.net/member.php?mod=logging&action=login&loginsubmit=yes&handlekey=login&loginhash='+loginhash+'&inajax=1' data={'formhash':formhash, 'referer':'http://www.discuz.net/forum.php', 'loginfield':'username', 'username':username, 'password':password, ', 'answer':'', 'seccodehash':'cSA', 'seccodemodid':'member::logging', 'seccodeverify':code} headers={','User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36','Content-Type':'application/x-www-form-urlencoded','Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8','Referer':'http://www.discuz.net/forum.php','Accept-Encoding':'gzip, deflate','Accept-Language':'zh-CN,zh;q=0.8'} session.headers.clear() session.headers.update(headers) r=session.post(url,data) print(r.text)
模拟登录:
(loginhash,formhash)=get_login_window() get_code(get_code_info()) code=input()#人工识别 :) check_code(code) #[CDATA[succeed]] login(loginhash,formhash,code,username,password) #欢迎您回来,现在将转入登录前页面
测试:
此页面只有在登录后才能显示
url='http://www.discuz.net/home.php?mod=space&do=pm'
标题为 ”用户名 - Discuz! 官方站 - Powered by Discuz!“
如果未登录,则标题为 ”提示信息 - Discuz! 官方站 - Powered by Discuz!“
session.headers.clear() r=session.get(url) p=r.text.find('<title>')+len('<title>') print(r.text[p:r.text.find('<',p)])
如果打印出了 ”用户名 - Discuz! 官方站 - Powered by Discuz!“,则证明登录成功。
最新文章
- MIT JOS学习笔记02:kernel 01(2016.10.28)
- Maven插件之maven-archetype-plugin
- SpringMVC学习系列-后记 结合SpringMVC和Hibernate-validator,根据后台验证规则自动生成前台的js验证代码
- FZU 2032 Log函数问题 模拟小数加法
- 【Delphi】注册快捷键
- SQL查询时间去除非工作日...
- C++拷贝构造函数详解 转
- myql查询创建表语句SHOW CREATE TABLE table_name
- switchover和failover
- VS下 dllimport与dllexport作用与区别
- VBS中解决路径带空格的三种方法
- C#中的foreach语句与枚举器接口(IEnumerator)及其泛型 相关问题
- ORB_SLAM2之Pangolin的安装与问题处理
- Quartz (二) 和Spring整合
- AdaBoost对实际数据分类的Julia实现
- [bzoj4813][Cqoi2017]小Q的棋盘
- AutoFac (控制反转IOC 与依赖注入DI)
- JS --- 如何获取一个对象的类型
- React native 之android的图标和启动图片
- python3之SQLAlchemy