本实验中咱们将经过分析登录流程并使用 Python 实现模拟登录到一个实验提供的网站,在实验过程当中将学习并实践 Python 的网络编程,Python 实现模拟登录的方法,使用 Firefox 抓包分析插件分析网络数据包等知识。html
模拟登陆能够帮助用户自动化完成不少操做,在不一样场合下有不一样的用处,不管是自动化一些平常的繁琐操做仍是用于爬虫都是一项很实用的技能。本课程经过 Firefox 和 Python 来实现,环境要求以下:前端
本项目中咱们将学习并实践如下知识点:python
为了节省时间,实验用到的材料已经提早制做完成,能够按照材料清单中给出的连接下载。web
实验网站源码:
http://labfile.oss.aliyuncs.com/courses/640/mysite.zip FireFox抓包插件: http://labfile.oss.aliyuncs.com/courses/640/live_http_headers.xpi
打开 Linux Xfce 终端并经过 wget
命令下载插件文件。下载成功后首选右键单击xpi文件-->使用FireFox打开,以后按照界面提示安装Live Http Header插件:正则表达式
按照提示重启以后,经过 打开菜单-->附加组件-->扩展
找到安装好的插件,点击 首选项
,勾选 Config 选项卡中的Open LiveHTTPHeaders in a new tab
选项以方便使用。django
因为实验楼会员环境中启动的 WebIDE 会占用 8000 端口,因此若是是实验楼会员,请先中止 codebox 进程后再部署下面的 Web 应用。编程
使用 ps -aux | grep codebox
查询得到 codebox 的进程号,而后使用 kill -9 进程号
中止 codebox 进程。执行过程见下图:api
首先安装demo依赖的web框架django,并测试是否安装成功:浏览器
$ sudo pip install django
$ python
>>> import django >>> django.VERSION (1, 10, 0, u'final', 1)
再经过unzip命令解压网站文件,并启动网站服务。安全
$ wget http://labfile.oss.aliyuncs.com/courses/640/mysite.zip $ unzip mysite.zip $ cd mysite $ ./manage.py runserver
启动成功后在浏览器中输入 http://localhost:8000/polls
看到登陆页面表示启动成功。
要经过编程实现登陆,首先须要理解通常Web应用的登录过程。
不一样的网站和应用登陆的安全性和复杂性都不一样,所以它们的登陆实现过程天然会存在差别,尽管如此,最基础,核心的过程依然是相同的。对于复杂的登陆过程(例如淘宝),对请求包和响应包的分析能力是很重要的。
浏览器有自带的分析工具,可是界面比较窄,考虑到实验环境界面较小,本课程选择了 Live Http Header,同窗们能够在本地选用任何本身喜欢的工具进行分析。咱们先经过示例页面分析一下简单的登录过程熟悉下分析过程。
http://localhost:8000/polls
打开登陆页。F10->工具->Live Http Header
)。shiyanlou
)并提交表单,登入系统。Live http Headers插件的Headers
选项卡中会列出抓取到的全部的http请求和响应头,一次请求的url、请求和响应之间经过空行隔开,不一样的请求之间经过虚线隔开。在按照2.1中的步骤登入系统后,咱们在列表中看到了2个请求。第一个请求核心内容以下(还记得HTTP协议的内容吗?):
POST /polls/login HTTP/1.1 //请求行 ******************** //此处省略其余请求头 Content-Type: application/x-www-form-urlencoded //请求实体类型 Content-Length: 41 //实体信息长度 name=shiyanlou&pwd=shiyanlou&commit=Login //实体内容 HTTP/1.0 302 Found //响应行,302重定向 Location: . //重定向的路径 ******************** //此处省略其余响应头 Set-Cookie: sessionid=aped4pzerxjgd3db0ixw0dowkgrdpsxb;expires=Thu, 15-Sep-2016 15:04:28 GMT; httponly; Max-Age=1209600; Path=/ //响应头,服务器回写cookie
为何是2个请求而不是1个呢?经过分析登陆请求发现,登录成功以后服务器发送了302重定向响应,服务器要求浏览器从新请求首页,这就产生了第二个请求。再来分析第二个请求,能够看到它相比登陆请求多了一个请求头:
Cookie: sessionid=aped4pzerxjgd3db0ixw0dowkgrdpsxb
这个 Cookie
中的 sessionid
是从上一个响应头中的 Set-Cookie
行中获取,因此值相同。session
的意思是会话,服务器经过session存储和维护与单个用户之间的会话信息。不一样的用户在服务器端有不一样的session,而且为了确保正常通讯,每一个用户的session都是惟一的。登陆过程其实就是在验证用户登陆信息后在session中存储用户登陆标识的过程。在开发web应用时,“登录成功”与“在session中存储用户登陆标识”是等价的。整个登陆流程以下图所示:
那么问题来了,服务器究竟是如何区别不一样用户的session的?为何登录成功会后要回写cookie呢?答案就是sessionid!每一个用户的session都有独立的、惟一的编号sessionid用来标识用户身份。用户登陆后,服务器经过从用户的session中读取登陆标识来进行身份认证,所以必需要知道sessionid来访问用户的session,而使用cookie正是知足这个需求的方法。服务器将须要浏览器存储的信息经过Set-Cookie
响应头发送给浏览器,浏览器会将cookie存储在本地,并在每次访问该网站时附带发送指定的cookie以知足服务器的需求,而经过cookie存储sessionid就是其中的一种应用。
对于服务器来讲,登陆=验证+写session。对于浏览器来讲,登陆=发送登陆信息+获取带sessionid的cookie。能够说,只要得到了sessionid,就算实现了模拟登陆。有了它咱们即可以游离于系统之中。
理解了登陆过程的原理和细节以后,开始用Python来编写模拟登录程序吧。在任意目录下新建login_base.py
文件,使用你喜欢的编辑器打开。实现流程以下:
不要忘记编写文件头、导入必要的依赖模块哦
#!/usr/bin/python #-*- coding:utf-8 -*- import urllib import urllib2 import cookielib
一个http请求由3部分组成:请求url、请求头以及请求实体(附带数据)。在Python的urllib2库中,由urllib2.Request对象描述一个request。其中url是一个字符串、请求头是一个dict,key是请求头名称,value是请求头的内容。至于请求数据就要分具体状况了,在表单提交这种场景下,请求数据类型为application/x-www-form-urlencoded
,意思就是通过url编码的表单数据,数据的组织形式是key=value
,多组键值对之间用&
分隔。登陆请求的实体部分以下:
Content-Type: application/x-www-form-urlencoded //请求实体类型 Content-Length: 41 //实体信息长度 name=shiyanlou&pwd=shiyanlou&commit=Login //实体内容
此时,咱们只须要使用dict来存储键值对,再用urllib.urlencode()
方法进行编码就能够了。最后建立urllib2.Request
对象,所有代码以下:
url = 'http://localhost:8000/polls/login' values = { 'name':'shiyanlou', 'pwd':'shiyanlou', 'commit':'Login' } headers = {'Referer':'http://localhost:8000/polls/show_login'} request = urllib2.Request(url,data=urllib.urlencode(values),headers=headers)
须要注意的是,在浏览器页面操做时,用户只须要输入用户名和密码,看起来好像只须要提交2个参数到服务器。但实际上前端页面通常都会提交其余元参数到服务器,大多数都是与服务端的访问api设计有关,还有一些控制参数。一个登陆请求包含5个以上的参数是再正常不过的事情,但并非每个参数都是登陆所必要的,有些参数即便没有也不会影响正常登陆。在实验demo中,commit这个参数就是必须提交的,不然会登陆失败。
Web 应用的资源都是有url的,只要得到了url就可以在任何地方引用。听起来很方便,但这可能会致使你的资源被别人盗用。为了访问量,把本身辛辛苦苦PS的一张美照放到我的站点上,却被别人的站点给引用走了,岂不是很气人?而HTTP的请求头Referer
就是为了解决这类问题而生,Referer描述了当前请求的发出者,也就是引用者,服务器能够经过Referer来判断当前的引用者是不是合法的引用者从而决定是否返回请求的资源。考虑到必定的安全性,通常的网站登陆都会限制Referer域来防止非法登陆。所以,为了成功登陆,咱们须要在request头中填写Referer头,内容从抓取的请求头中照搬来便可。
通常状况下,打开url默认使用urllib2的urlopen()
函数,可是它不能处理cookie。这里咱们须要本身建立可以存储cookie的opener。python内置有cookielib库来处理cookie,其中的MozillaCookieJar能够将cookie存储到文件中,并能够从文件中读取cookie。先建立MozillaCookieJar对象,再使用urllib2.HTTPCookieProcessor
建立cookie处理器,最后使用urllib2.build_opener
建立opener。接下来就能够用opener发送请求并存储cookie了。代码以下:
# 建立opener cookies = cookielib.MozillaCookieJar('my_cookies.txt') # 指定cookie的存储文件 cookie_handler = urllib2.HTTPCookieProcessor(cookies) opener = urllib2.build_opener(cookie_handler) # 发送请求 & 保存cookie response = opener.open(request) cookies.save() print response.code print response.read()
若是登录成功,就能够在指定的文件my_cookies.txt中看到sessionid了。
localhost.local FALSE / FALSE 1474019663 sessionid vejomjbliggkwblzfagobv3u8foik6am
登陆系统只不过是一个开始,使用sessionid访问系统服务才是最终目的。举一个简单的例子,咱们的网站demo的首页中由一个查询系统当前时间的接口(在现实生活中多是更有用的,好比信息查询),咱们在登陆后点击它,能够看到它的url是http://localhost:8000/polls/date
,这是只为登陆用户提供的服务。若是咱们如今返回首页,退出登陆以后直接在地址栏中输入这个url,页面将会302重定向至登陆页,没法看到当前时间。
在咱们模拟登陆成功后,就能够直接经过opener打开这个url来使用这项系统服务。代码实现以下:
url2 = 'http://localhost:8000/polls/date' response2 = opener.open(url2) print response2.code print response2.read()
若是有是在另一个py文件中使用这个cookie的话,再打开url以前须要先载入cookie:
# 载入cookie cookie = cookielib.MozillaCookieJar() cookie.load('my_cookies.txt') opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie))
固然,以上只是一个简单的示例,只是为了说明原理。实际应用中的请求中会有各类各样的请求参数、元数据参数甚至是多样的验证cookie,只有理解了原理才可以以不变应万变。
是否是原理听懂了,可是只实现demo感受不够过瘾?咱们来一块儿作一作下面这个稍微难一点的练习吧。
已知信息:
要求:
管理员的登陆请求的请求以下:
POST /admin/login/?next=/admin/ HTTP/1.1 Referer: http://localhost:8000/admin/login/?next=/admin/ Cookie: csrftoken=adxAiQKnRYgQoS4RmoooCesA7mBgJtwVnsd98nttE1arcBAnGwRbILLvWeS5xLfM Content-Type: application/x-www-form-urlencoded Content-Length: 137 csrfmiddlewaretoken=tsxs3KcOUt4ZdMg1gMBCKkNV8JTxZigaGHd1ThVUHwYA1vMxAU4pQR6QXBamNAZ1&username=admin&password=djangoadmin&next=%2Fadmin%2F HTTP/1.0 302 Found Vary: Cookie Last-Modified: Fri, 02 Sep 2016 09:24:46 GMT Location: /admin/ Content-Type: text/html; charset=utf-8 Set-Cookie: csrftoken=seowCkz5vprAPZbkqF7M4QuzYZPKgoUrsO5nE3WWxtYnc5NRKB34rmmrTAIDdCaK; expires=Fri, 01-Sep-2017 09:24:46 GMT; Max-Age=31449600; Path=/ Set-Cookie: sessionid=cb6z7xlu31uxt8kdupfh0td43ewhgvpx; expires=Fri, 16-Sep-2016 09:24:46 GMT; httponly; Max-Age=1209600; Path=/
不难观察到登陆请求中附带了csrftoken
这个cookie,表单数据中必须提交csrfmiddlewaretoken
,username
,password
这三个参数,最后的next
参数是url中附带的,不须要额外添加。必须获得前两个参数才可以成功登录,这两个参数从名字上来看都用来做认证令牌,应该是由服务器生成的。其中的cookie确定是来自上一个response的回写,而从csrfmiddlewaretoken
的位置来看,应该是在登录表单当中。注销登录,从新进入登陆页面,点击右键查看源代码发现了这个输入域(每次请求的值都是不一样的):
<input type='hidden' name='csrfmiddlewaretoken' value='PSM5KbmRGdHraaVXK9UEzsWmLYlHxYjUPstWMUJIIheexgxu45QWWYOeGzeAuczd' />
这样一来咱们的思路就很清晰了:
解析页面可使用python的正则表达式re模块
或者是比较火的beautifulsoup库
。但愿同窗们先本身动手挑战一下,欢迎再课程回复中和我讨论,届时我将提供登陆代码。
模拟登录的关键是弄清楚登陆请求须要提交的参数,重点是要获取带有sessionid的cookie,最终目的是不受限制的访问系统提供的有价值的服务。
只有分析清楚登录过程才能达到最终目的,这就须要具有强的HTTP包分析能力,理解HTTP协议,并配合包分析工具(FireBug,Live Http Header,Burp Suite等)解析出关键参数,有时须要编辑和重发包来删减掉没必要要的多余参数。下面给出一些提升的参考资料,学有余力的同窗能够深刻研究: