- <?php
- session_start();
- $_SESSION['foo'] = 'bar';
- ?>
- <a href="continue.php">continue.php</a>
假如用户点击start.php中的链接访问continue.php,那么在continue.php中就可以通过$_SESSION['foo']获取在start.php中的定义的值’bar’。看下面的示例代码2: 示例代码2 – continue.php - <?php
- session_start();
- echo $_SESSION['foo']; /* bar */
- ?>
是不是非常简单,但是我要指出的话,如果你真的这样来写代码的话,说明你对php底层的对于session的实现机制还不是非常了解透彻。在不了解php内部给你自动做了多少事情的情况下,你会发现如果程序出错的话,这样的代码将变的很难调试,事实上,这样的代码也完全没有安全性可言。 Session的安全性问题 一直以来很多开发者都认为php内置的session管理机制是具有一定的安全性,可以对一般的session攻击起到防御。事实上,这是一种误解,php团队只实现了一种方便有效的机制。具体的安全措施,应该有应用程序的开发团队来实施。 就像开篇谈到的,没有最好的解决方案,只有最合适你的方案。 现在,我们来看下一个比较常规的针对session的攻击: 用户访问http://www.example.org,并且登录。 example.org的服务器设置指示客户端设置相关cookie – PHPSESSID=12345 攻击者这时访问http://www.example.org/,并且在请求中携带了对应的cookie – PHPSESSID=12345 这样情况下,因为example.orge的服务器通过PHPSESSID来辨认对应的用户的,所以服务器错把攻击者当成了合法的用户。 整个过程的描述,请看下面的示例图: 当然这种攻击的方式,前提条件是攻击者必须通过某种手段固定,劫持或者猜测出某个合法用户的PHPSESSID。虽然这看起来难度很高,但是也不是不可能的事情。 安全性的加强 有很多技术可以用来加强Session的安全性,主要思想就是要使验证的过程对于合法用户来说,越简单越好,然后对于攻击者来说,步骤要越复杂越好。当然,这似乎是比较难于平衡的,要根据你应用程序的具体设计来做决策。 最简单的居于HTTP/1.1请求包括请求行以及一些Host的头部: GET / HTTP/1.1 Host: example.org 如果客户端通过PHPSESSID传递相关的session标识符,可以将PHPSESSID放在cookie头部中进行传递: GET / HTTP/1.1 Host: example.org Cookie: PHPSESSID=12345 同样地,客户端也可以将session标识符放在请求的url中进行传递。 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等必须包含的头部,还包含了一些可选的头部.举一个例子,看下面的一个请求: 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。因为这些头部不是必须的,所以完全依赖他们在你的应用程序中发挥作用是不太明智的。但是,如果一个用户的浏览器确实发送了这些头部到服务器,那么可以肯定的是在接下来的同一个用户通过同一个浏览器发送的请求中,必然也会携带这些头部。当然,这其中也会有极少数的特殊情况发生。假如以上例子是由一个当前的跟服务器建立了会话的用户发出的请求,考虑下面的一个请求: GET / HTTP/1.1 Host: example.org Cookie: PHPSESSID=12345 User-Agent: Mozilla/5.0 因为有相同的session id包含在请求的Cookie头部中,所以相同的php session将会被访问到。但是,请求里的User-Agent头部跟先前的请求中的信息是不同的,系统是否可以假定这两个请求是同一个用户发出的? 像这种情况下,发现浏览器的头部改变了,但是不能肯定这是否是一次来自攻击者的请求的话,比较好的措施就是弹出一个要求输入密码的输入框让用户输入,这样的话,对用户体验的影响不会很大,又能很有效地防止攻击。 当然,你可以在系统中加入核查User-Agent头部的代码,类似示例3中的代码: 示例代码3 - <?php
- session_start();
- if (md5($_SERVER['HTTP_USER_AGENT']) != $_SESSION['HTTP_USER_AGENT'])
- { /* 弹出密码输入框 */ exit;
- }
- ?>
|