PHP cookie和session的分析
作者:余超 Email:yuchao86@gmail.com
1. PHP 的 COOKIE cookie 是一种在远程浏览器端储存数据并以此来跟踪和识别用户的机制。
PHP 在 http 协议的头信息里发送 cookie, 因此 setcookie() 函数必须在其它信息被输出到浏览器前调用,这和对 header() 函数的限制类似。
1.1 设置 cookie :
可以用 setcookie() 或 setrawcookie() 函数来设置 cookie 。也可以通过向客户端直接发送 http 头来设置 .
1.1.1 使用 setcookie() 函数 设置cookie:
bool setcookie ( string name [, string value [, int expire [, string path [, string domain [, bool secure [, bool httponly]]]]]] )
name: cookie 变量名
value: cookie 变量的值
expire: 有效期结束的时间 ,
path: 有效目录 ,
domain: 有效域名 , 顶级域唯一
secure: 如果值为 1, 则 cookie 只能在 https 连接上有效 , 如果为默认值 0, 则 http 和 https 都可以 .
例子 :
< ? php
$ value = 'something from somewhere' ;
setcookie ( "TestCookie" , $ value ) ; /* 简单cookie设置 */
setcookie ( "TestCookie" , $ value , time ( ) + 3600) ; /* 有效期1个小时 */
setcookie ( "TestCookie" , $ value , time ( ) + 3600, "/~rasmus/" , ".example.com" , 1) ; /* 有效目录 /~rasmus,有效域名example.com及其所有子域名 */
? > 设置多个 cookie 变量 : setcookie('var[a]','value'); 用数组来表示变量 , 但他的下标不用引号 . 这样就可以用 $_COOKIE[‘var’][‘a’] 来读取该 COOKIE 变量 .
1.1.2. 使用 header() 设置cookie ;
header("Set-Cookie: name=$value[;path=$path[; domain=xxx.com [;...]]");
后面的参数和上面列出 setcookie 函数的参数一样 .
比如: $ value = 'something from somewhere' ;
header(" Set-Cookie:name= $value ");
1.2 Cookie的读取: 直接用php内置超级全局变量 $_COOKIE就可以读取浏览器端的cookie.
上面例子中设置了cookie"TestCookie",现在我们来读取: print $_COOKIE[ 'TestCookie' ] ; COOKIE是不是被输出了?! 1.3 删除 cookie
只需把有效时间设为小于当前时间 , 和把值设置为空 .例如:
setcookie("name","",time()-1);
用 header() 类似 . 1.4 常见问题解决 : 1) 用setcookie()时有错误提示,可能是因为调用setcookie()前面有输出或空格.也可能你的文档使从其他字符集转换过来,文档后面可能带有BOM签名(就是在文件内容添加一些隐藏的 BOM 字符).解决的办法就是使你的文档不出现这种情况.还有通过使用ob_start()函数有也能处理一点.
2) $_COOKIE 受 magic_quotes_gpc 影响 , 可能自动转义
3) 使用的时候 , 有必要测试用户是否支持 cookie
<!--[if !supportLineBreakNewLine]--> 1.5 cookie 工作机理 : 有些学习者比较冲动,没心思把原理研究,所以我把它放后面.
a) 服务器通过随着响应发送一个 http 的 Set-Cookie 头 , 在客户机中设置一个 cookie( 多个 cookie 要多个头 ).
b) 客户端自动向服务器端发送一个 http 的 cookie 头 , 服务器接收读取 .
HTTP/1.x 200 OK
X-Powered-By: PHP/5.2.1
Set-Cookie: TestCookie = something from somewhere ; path=/
Expires: Thu, 19 Nov 2007 18:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-type: text/html 这一行实现了cookie功能,收到这行后
Set-Cookie: TestCookie = something from somewhere ; path=/
浏览器将在客户端的磁盘上创建一个cookie文件,并在里面写入: TestCookie = something from somewhere ;
/ 这一行就是我们用setcookie( 'TestCookie' , 'something from somewhere' , '/' ) ; 的结果.也就是用header('Set-Cookie: TestCookie=something from somewhere; path=/' ) ; 的结果.
<!--[endif]-->
2. PHP 的 Session
session 使用过期时间设为 0 的 cookie, 并且将一个称为 session ID 的唯一标识符 ( 一长串字符串 ) ,在服务器端同步生成一些session文件(可以自己定义session的保存类型),与用户机关联起来 .web 应用程序存贮与这些 session 相关的数据 , 并且让数据随着用户在页面之间 传递 .
访问网站的来客会被分配一个唯一的标识符,即所谓的会话 ID。它要么存放在客户端的 cookie,要么经由 URL 传递。
会 话支持允许用户注册任意数目的变量并保留给各个请求使用。当来客访问网站时,PHP 会自动(如果 session.auto_start 被设为 1)或在用户请求时(由 session_start() 明确调用或 session_register() 暗中调用)检查请求中是否发送了特定的会话 ID。如果是,则之前保存的环境就被重建。
2.1 sessionID 的传送
2.1.1 通过 cookie 传送 sessin ID
使用 session_start() 调用session,服务器端在生成session文件的同时,生成session ID哈希值和默认值为 PHPSESSID的session name,并向客户端发送变量为 (默认的是)PHPSESSID(session name ),值为一个128位的哈希值.服务器端将通过该cookie与客户端进行交互.
session变量的值经php内部系列化后保存在服务器机器上的文本文件中,和客户端的变量名默认情况下为 PHPSESSID的coolie进行对应交互.
即服务器自动发送了http头:header('Set-Cookie: session_name()=session_id() ; path=/');
即 setcookie( session_name() , session_id()) ;
当从该页跳转到的新页面并调用 session_start() 后 ,PHP 将检查与给定 ID 相关联的服务器端存贮的 session 数据 , 如果没找到 , 则新建一个数据集 .
2.1.2 通过 URL 传送 session ID
只有在用户禁止使用 cookie 的时候才用这种方法 , 因为浏览器 cookie 已经通用 , 为安全起见 , 可不用该方法 .<a href="p.php?<?php print session_name() ?> =<?php print session_id() ?> ">xxx</a>,也可以通过POST来传递session值. 2.2 session基本用法实例 < ? php
// page1.php
session_start ( ) ;
echo 'Welcome to page #1' ;
/* 创建session变量并给session变量赋值 */
$ _SESSION [ 'favcolor' ] = 'green' ;
$ _SESSION [ 'animal' ] = 'cat' ;
$ _SESSION [ 'time' ] = time ( ) ;
// 如果客户端使用cookie,可直接传递session到page2.php
echo '<br /><a href="page2.php">page 2</a>' ;
// 如果客户端禁用cookie
echo '<br /><a href="page2.php?' . SID . '">page 2</a>' ;
/*
默认php5.2.1下,SID只有在cookie被写入的同时才会有值,如果该session
对应的cookie已经存在,那么SID将为(未定义)空
*/
? >
< ? php
// page2.php
session_start ( ) ;
print $ _SESSION [ 'animal' ] ; // 打印出单个session
var_dump ( $ _SESSION ) ; // 打印出page1.php传过来的session值
? >
2.3 使用 session 函数控制页面缓存 .
很多情况下,我们要确定我们的网页是否在客户端缓存,或要设置缓存的有效时间,比如我们的网页上有些敏感内容并且要登录才能查看,如果缓存到本地了,可以直接打开本地的缓存就可以不登录而浏览到网页了.
使用 session_cache_limiter('private'); 可以控制页面客户端缓存 , 必须在 session_start() 之前调用 .
更多参数见 http://blog.chinaunix.net/u/27731/showart.php?id=258087 的客户端缓存控制 .
控制客户端缓存时间 用 session_cache_expire(int); 单位 (s). 也要在 session_start() 前调用 .
这只是使用session的情况下控制缓存的方法,我们还可以在header()中控制控制页面的缓存.
2.4 删除 session
要三步实现 .
<?php
session_destroy(); // 第一步 : 删除服务器端 session 文件 , 这使用
setcookie(session_name(),'',time()-3600); // 第二步 : 删除实际的 session:
$_SESSION = array(); // 第三步 : 删除 $_SESSION 全局变量数组
?>
2.5 session 在 PHP 大型 web 应用中的使用
对于访问量大的站点 , 用默认的 session 存贮方式并不适合 , 目前最优的方法是用数据库存取 session. 这时 , 函数 bool session_set_save_handler ( callback open, callback close, callback read, callback write, callback destroy, callback gc ) 就是提供给我们解决这个问题的方案 .
该函数使用的 6 个函数如下 : 1. bool open() 用来打开会话存储机制 , 2. bool close() 关闭会话存储操作 . 3. mixde read() 从存储中装在 session 数据时使用这个函数 4. bool write() 将给定 session ID 的所有数据写到存储中 5. bool destroy() 破坏与指定的会话 ID 相关联的数据 6. bool gc() 对存储系统中的数据进行垃圾收集 例子见php手册 session_set_save_handler() 函数 .
如果用类来处理 , 用
session_set_save_handler(
array('className',' open '),
array('className',' close '),
array('className',' read '),
array('className',' write '),
array('className','destroy'),
array('className','gc'),
)
调用 className 类中的 6 个静态方法 .className 可以换对象就不用调用静态方法 , 但是用静态成员不用生成对象 , 性能更好 .
2.6 常用 session 函数 :
bool session_start(void); 初始化 session
bool session_destroy(void) : 删除服务器端 session 关联文件 。
string session_id() 当前 session 的 id
string session_name() 当前存取的 session 名称 , 也就是客户端保存 session ID 的 cookie 名称 . 默认 PHPSESSID 。
array session_get_cookie_params() 与这个 session 相关联的 session 的细节 .
string session_cache_limiter() 控制使用 session 的页面的客户端缓存
ini session_cache_expire() 控制客户端缓存时间
bool session_destroy() 删除服务器端保存 session 信息的文件
void session_set_cookie_params ( int lifetime [, string path [, string domain [, bool secure [, bool httponly]]]] )设置与这个 session 相关联的 session 的细节
bool session_set_save_handler ( callback open, callback close, callback read, callback write, callback destroy, callback gc ) 定义处理 session 的函数 ,( 不是使用默认的方式 )
bool session_regenerate_id([bool delete_old_session]) 分配新的 session id
2.7 session 安全问题
攻击者通过投入很大的精力尝试获得现有用户的有效会话 ID, 有了会话 id, 他们就有可能能够在系统中拥有与此用户相同的能力 .
因此 , 我们主要解决的思路是效验 session ID 的有效性 . < ? php
if ( ! isset ( $ _SESSION [ 'user_agent' ] ) ) {
$ _SESSION [ 'user_agent' ] = $ _SERVER [ 'REMOTE_ADDR' ] . $ _SERVER [ 'HTTP_USER_AGENT' ] ;
}
/* 如果用户session ID是伪造 */
elseif ( $ _SESSION [ 'user_agent' ] ! = $ _SERVER [ 'REMOTE_ADDR' ] . $ _SERVER [ 'HTTP_USER_AGENT'] ) {
session_regenerate_id ( ) ;
}
? >
2.8 Session 通过 cookie 传递和通过SID传递的不同 :
在 php5.2.1 的 session 的默认配置的情况下 , 当生成 session 的同时 , 服务器端将在发送 header set-cookie 同时 生成预定义超级全局变量 SID( 也就是说 , 写入 cookie 和抛出 SID 是等价的 .), 当 $_COOKIE['PHPSESSID'] 存在以后 , 将不再写入cookie, 也不再生成超级全局变量 SID, 此时 ,SID 将是空的 .
2.9 session使用实例
< ? php
/**
* 效验session的合法性
*
*/
function sessionVerify( ) {
if ( ! isset ( $ _SESSION [ 'user_agent' ] ) ) {
$ _SESSION [ 'user_agent' ] = MD5 ( $ _SERVER [ 'REMOTE_ADDR' ]
. $ _SERVER [ 'HTTP_USER_AGENT' ] ) ;
}
/* 如果用户session ID是伪造,则重新分配session ID */
elseif ( $ _SESSION [ 'user_agent' ] ! = MD5 ( $ _SERVER [ 'REMOTE_ADDR' ]
. $ _SERVER [ 'HTTP_USER_AGENT' ] ) ) {
session_regenerate_id ( ) ;
}
}
/**
* 销毁session
* 三步完美实现,不可漏
*
*/
function sessionDestroy( ) {
session_destroy ( ) ;
setcookie ( session_name ( ) , '' , time ( ) - 3600) ;
$ _SESSION = array ( ) ;
}
? >
注明:
session 出现头信息已经发出的原因与cookie一样.
在php5中,所有php session 的注册表配置选项都是编程时可配置的,一般情况下,我们是不用修改其配置的.要了解php的session注册表配置选项,请参考手册的Session 会话处理函数处. session的保存数据的时候,是通过系列化$_SESSION数组来存贮,所以有系列化所拥有的问题,可能有特殊字符的值要用base64_encode函数编码,读取的时候再用base64_decode解码
|