【博客360weboy - http://www.360weboy.com】 php
有一点咱们必须认可,大多数web应用程序都离不开session的使用。这篇文章将会结合php以及http协议来分析如何创建一个安全的会话管理机制。咱们先简单的了解一些http的知识,从而理解该协议的无状态特性。而后,学习一些关于cookie的基本操做。最后,我会一步步阐述如何使用一些简单,高效的方法来提升你的php应用程序的安全性以及稳定行。 html
我想大多数的php初级程序员必定会认为php默认的session机制的安全性彷佛是有必定保障的,事实刚好相反 – php团队只是提供了一套便捷的session的解决方案提供给程序员使用,至于安全性的话,应该由程序员来增强,这是应用程序开发团队的责任。由于,这里面的方法不少,能够这么说吧,没有最好,只有更好。攻击的方式在不断变化,防守方也须要不断变招,因此,我我的认为php团队的作法仍是比较明智的。 程序员
Http是一种无状态性的协议。这是由于此种协议不要求浏览器在每次请求中标明它本身的身份,而且浏览器以及服务器之间并无保持一个持久性的链接用于多个页面之间的访问。当一个用户访问一个站点的时候,用户的浏览器发送一个http请求到服务器,服务器返回给浏览器一个http响应。其实很简单的一个概念,客户端一个请求,服务器端一个回复,这就是整个基于http协议的通信过程。 web
由于web应用程序是基于http协议进行通信的,而咱们已经讲过了http是无状态的,这就增长了维护web应用程序状态的难度, 对于开发者来讲,是一个不小的挑战。Cookies是做为http的一个扩展诞生的,其主要用途是弥补http的无状态特性,提供了一种保持客户端与服务器端之间状态的途径,可是因为出于安全性的考虑,有的用户在浏览器中是禁止掉cookie的。这种状况下,状态信息只能经过url中的参数来传递到服务器端,不过这种方式的安全性不好。事实上,按照一般的想法,应该有客户端来代表本身的身份,从而和服务器之间维持一种状态,可是出于安全性方面的考虑,咱们都应该明白一点 – 来自客户端的信息都是不能彻底信任的。 apache
尽管这样,针对维持web应用程序状态的问题,相对来讲,仍是有比较优雅的解决方案的。不过,应该说是没有完美的解决方案的,再好的解决方案也不可能适用全部的状况。这篇文章将介绍一些技术。这些技术能够用来比较稳定地维持应用程序的状态以及抵御一些针对session的攻击,好比会话劫持。而且你能够学习到cookie是怎样工做的,php 的session作了那些事情,以及怎样才能劫持session。 数组
如何才能保持web应用程序的状态以及选择最合适的解决方案呢?在回答这个问题以前,必须得先了解web的底层协议 – Hypertext Transfer Protocol (HTTP)。 浏览器
当用户访问http://example.com这个域名的时候,浏览器就会自动和服务器创建tcp/ip链接,而后发送http请求到example.com的服务器的80端口。该个请求的语法以下所示: 缓存
1
2 |
GET
/ HTTP
/
1.1
Host : example .org |
以上第一行叫作请求行,第二个参数(一个反斜线在这个例子中)表示所请求资源的路径。反斜线表明了根目录;服务器会转换这个根目录为服务器文件系统中的一个具体目录。 安全
Apache的用户经常使用DocumentRoot这个命令来设置这个文档根路径。若是请求的url是http://example.org/path/to/script.php,那么请求的路径就是/path/to/script.php。假如document root 被定义为usr/lcoal/apache/htdocs的话,整个请求的资源路径就是/usr/local/apache/htdocs/path/to/script.php。 服务器
第二行描述的是http头部的语法。在这个例子中的头部是Host, 它标识了浏览器但愿获取资源的域名主机。还有不少其它的请求头部能够包含在http请求中,好比user-Agent头部,在php能够经过$_SERVER['HTTP_USER_AGENT']获取请求中所携带的这个头部信息。
可是遗憾的是,在这个请求例子中,没有任何信息能够惟一标识当前这个发出请求的客户端。有些开发者借助请求中的ip头部来惟一标识发出这次请求的客户端,可是这种方式存在不少问题。由于,有些用户是经过代理来访问的,好比用户A经过代理B链接网站www.example.com, 服务器端获取的ip信息是代理B分配给A的ip地址,若是用户这时断开代理,而后再次链接代理的话,它的代理ip地址又再次改变,也就说一个用户对应了多个ip地址,这种状况下,服务器端根据ip地址来标识用户的话,会认为请求是来自不一样的用户,事实上是同一个用户。 还用另一种状况就是,好比不少用户是在同一个局域网里经过路由链接互联网,而后都访问www.example.com的话,因为这些用户共享同一个外网ip地址,这会致使服务器认为这些用户是同一个用户发出的请求,由于他们是来自同一个ip地址的访问。
保持应用程序状态的第一步就是要知道如何来惟一地标识每一个客户端。由于只有在http中请求中携带的信息才能用来标识客户端,因此在请求中必须包含某种能够用来标识客户端惟一身份的信息。Cookie设计出来就是用来解决这一问题的。
若是你把Cookies当作为http协议的一个扩展的话,理解起来就容易的多了,其实本质上cookies就是http的一个扩展。有两个http头部是专门负责设置以及发送cookie的,它们分别是Set-Cookie以及Cookie。当服务器返回给客户端一个http响应信息时,其中若是包含Set-Cookie这个头部时,意思就是指示客户端创建一个cookie,而且在后续的http请求中自动发送这个cookie到服务器端,直到这个cookie过时。若是cookie的生存时间是整个会话期间的话,那么浏览器会将cookie保存在内存中,浏览器关闭时就会自动清除这个cookie。另一种状况就是保存在客户端的硬盘中,浏览器关闭的话,该cookie也不会被清除,下次打开浏览器访问对应网站时,这个cookie就会自动再次发送到服务器端。一个cookie的设置以及发送过程分为如下四步:
这个通信过程也能够用如下下示意图来描述:
在客户端的第二次请求中包含的Cookie头部中,提供给了服务器端能够用来惟一标识客户端身份的信息。这时,服务器端也就能够判断客户端是否启用了cookies。尽管,用户可能在和应用程序交互的过程当中忽然禁用cookies的使用,可是,这个状况基本是不太可能发生的,因此能够不加以考虑,这在实践中也被证实是对的。
除了cookies,客户端还能够将发送给服务器的数据包含在请求的url中,好比请求的参数或者请求的路径中。 咱们来看一个例子:
1
2 |
GET
/index
.php?foo
=bar HTTP
/
1.1
Host : example .org |
以上就是一个常规的http get 请求,该get请求发送到example.org域名对应的web 服务器下的index.php脚本, 在index.php脚本中,能够经过$_GET['foo']来获取对应的url中foo参数的值,也就是’bar’。大多数php开发者都称这样的数据会GET数据,也有少数称它为查询数据或者url变量。可是你们须要注意一点,不是说GET数据就只能包含在HTTP GET类型的请求中,在HTTP POST类型的请求中一样能够包含GET数据,只要将相关GET数据包含在请求的url中便可,也就是说GET数据的传递不依赖与具体请求的类型。
另一种客户端传递数据到服务器端的方式是将数据包含在http请求的内容区域内。 这种方式须要请求的类型是POST的,看下面一个例子:
1
2 3 4 5 6 |
POST
/index
.php HTTP
/
1.1
Host : example .org Content -Type : application /x -www -form -urlencoded Content -Length : 7 foo =bar |
在这种状况下,在脚本index.php能够经过调用$_POST['foo']来获取对应的值bar。开发者称这个数据为POST数据,也就是你们熟知的form以post方式提交请求的方式。
在一个请求中,能够同时包含这两种形式的数据:
1
2 3 4 5 |
POST
/index
.php?myget
=foo HTTP
/
1.1
Host : example .orgContent -Type : application /x -www -form -urlencoded Content -Length : 11 mypost =bar |
这两种传递数据的方式,比起用cookies来传递数据更稳定,由于cookie可能被禁用,可是以GET以及POST方式传递数据时,不存在这种状况。咱们能够将PHPSESSID包含在http请求的url中,就像下面的例子同样:
1
2 |
GET
/index
.php?PHPSESSID
=
12345 HTTP
/
1.1
Host : example .org |
以这种方式传递session id的话,能够跟用cookie头部传递session id同样,达到一样的效果, 可是,缺点就是须要开发者认为地将session id附加在url中或者做为隐藏字段加入到表单中。不像cookie同样,只要服务器端指示客户端建立cookie成功之后,客户端在后续的请求中,会自动第将对应的没有过时的cookie传递给服务器端。固然,php在开启session.use_trans_sid后,也能够自动地将session id 附加在url中以及表单的隐藏字段中,可是这个选项不建议开启,由于存在安全问题。这样的话,容易泄露session id, 好比有的用户会bookmark一个url或者分享一个url,那么session id也就暴露了,加入这个session id尚未过时,那是有必定的安全问题存在的,除非服务器端,除了session id外,还附加了其它方式进行验证用户的合法性!
尽管以POST的方式来传递session id的话,相对GET的方式来讲,会安全的多。可是,这种方式的缺点就是比较麻烦,由于这样的话,在你的应用程序中比较将全部的请求都转换成post的请求,这显然是不太合适的。
直到如今,我只讨论了如何维护应用程序的状态,只是简单地涉及到了若是保持请求之间的关系。接下来,我阐述下在实际中用到比较多的技术 – Session的管理。涉及到session的管理,就不是单单地维持各个请求之间的状态,还须要维持会话期间针对每一个特定用户使用到的数据。咱们经常把这种数据叫作session数据,由于这些数据是跟某个特定用户与服务器之间的会话相关联的。若是你使用php内置的session的管理机制,那么session数据通常是保存在/tmp这个服务器端的文件夹中,而且其中的session数据会被自动地保存到超级数组$_SESSION中。一个最简单的使用session的例子,就是将相关的session数据从一个页面传递(注意:实际传递的是session id)到另外一个页面。下面用示例代码1, start.php, 对这个例子加以演示:
1
2 3 4 5 6 |
假如用户点击start.php中的连接访问continue.php,那么在continue.php中就能够经过$_SESSION['foo']获取在start.php中的定义的值’bar’。看下面的示例代码2:
1
2 3 4 |
是否是很是简单,可是我要指出的话,若是你真的这样来写代码的话,说明你对php底层的对于session的实现机制还不是很是了解透彻。在不了解php内部给你自动作了多少事情的状况下,你会发现若是程序出错的话,这样的代码将变的很难调试,事实上,这样的代码也彻底没有安全性可言。
一直以来不少开发者都认为php内置的session管理机制是具备必定的安全性,能够对通常的session攻击起到防护。事实上,这是一种误解,php团队只实现了一种方便有效的机制。具体的安全措施,应该有应用程序的开发团队来实施。 就像开篇谈到的,没有最好的解决方案,只有最合适你的方案。
如今,咱们来看下一个比较常规的针对session的攻击:
整个过程的描述,请看下面的示例图:
固然这种攻击的方式,前提条件是攻击者必须经过某种手段固定,劫持或者猜想出某个合法用户的PHPSESSID。虽然这看起来难度很高,可是也不是不可能的事情。
有不少技术能够用来增强Session的安全性,主要思想就是要使验证的过程对于合法用户来讲,越简单越好,而后对于攻击者来讲,步骤要越复杂越好。固然,这彷佛是比较难于平衡的,要根据你应用程序的具体设计来作决策。
没有时间写下去,待续。。。
最简单的居于HTTP/1.1请求包括请求行以及一些Host的头部:
1
2 |
GET
/ HTTP
/
1.1
Host : example .org |
若是客户端经过PHPSESSID传递相关的session标识符,能够将PHPSESSID放在cookie头部中进行传递:
1
2 3 |
GET
/ HTTP
/
1.1
Host : example .org Cookie : PHPSESSID = 12345 |
一样地,客户端也能够将session标识符放在请求的url中进行传递。
1
2 |
GET /?PHPSESSID=12345
HTTP/1.1Host: example.org |
固然,session标识符也能够包含在POST数据中,可是这对用户体验有影响,因此这种方式不多采用。
由于来自TCP/IP信息也不必定能够彻底信任的,因此,对于web开发者来讲,利用TCP/IP中的信息来增强安全性也是不太合适的。 不过,攻击者也必须提供一个合法用户的惟一的标识符,才能假扮成合法用户进入系统。所以,看起来惟一可以有效的保护系统的措施,就是尽可能地隐藏session标识符或者使之难于猜想出来。最好就是二者都能实施。
PHP会自动生成一个随机的session ID,基原本说是不可能被猜想出来的,因此这方面的安全仍是有必定保障的。可是,要防止攻击者获取一个合法的session ID是至关困难的,这基本上不是开发者所能控制的。
事实上,许多状况下都有可能致使session ID的泄露。 好比说,若是经过GET数据来传递session ID的话,就有可能暴露这个敏感的身份信息。由于,有的用户可能会将带有session ID的连接缓存,收藏或者发送在邮件内容中。Cookies是一种像相对来讲安全一点的机制,可是用户是能够在客户端中禁止掉cookies的!在一些IE的版本中也有比较严重的安全漏洞,比较有名的就是会泄露cookies给一些有安全隐患的邪恶站点。
所以,做为一个开发者,能够确定session ID是不能被猜想出来的,可是仍是有可能被攻击者使用某些方法获取到。因此,必须采起一些额外的安全措施来防止此类状况在你的应用程序中发生。
实际上,一个标准的HTTP请求中除了Host等必须包含的头部,还包含了一些可选的头部.举一个例子,看下面的一个请求:
1
2 3 4 5 6 7 |
GET
/ HTTP
/
1.1
Host : example .org Cookie : PHPSESSID = 12345 User -Agent : Mozilla / 5.0 (Macintosh ; U ; Intel Mac OS X ; en -US ; rv :1 .8 .1 .1 ) Gecko / 20061204 Firefox /2 .0 .0 .1 Accept : text /html ;q = 0.9 , * /*;q=0.1 Accept-Charset: ISO-8859-1, utf-8;q=0.66, *;q=0.66 Accept-Language: en |
咱们能够看到,在以上的一个请求例子中包含了四个额外的头部,分别是User-Agent, Accept, Accept-Charset以及Accept-Language。由于这些头部不是必须的,因此彻底依赖他们在你的应用程序中发挥做用是不太明智的。可是,若是一个用户的浏览器确实发送了这些头部到服务器,那么能够确定的是在接下来的同一个用户经过同一个浏览器发送的请求中,必然也会携带这些头部。固然,这其中也会有极少数的特殊状况发生。假如以上例子是由一个当前的跟服务器创建了会话的用户发出的请求,考虑下面的一个请求:
【博客360weboy - http://www.360weboy.com】