PHP 中的会话支持由一种将特定数据保留用于之后的请求的方法组成。这样可以使用户建立更灵活的应用并提高网站的吸引力。
访问网站的来客会被分配一个唯一的标识符,即所谓的会话 ID。它要么存放在客户端的 cookie,要么经由 URL 传递。
会话支持允许用户注册任意数目的变量并保留给各个请求使用。当来客访问网站时,PHP 会自动(如果 session.auto_start 被设为 1)或在用户请求时(由 session_start() 明确调用或 session_register() 暗中调用)检查请求中是否发送了特定的会话 ID。如果是,则之前保存的环境就被重建。
如果确实启用了 session.auto_start,则不能将对象放入会话中,因为类定义必须在启动会话之前加载以在会话中重建对象。
请求结束后所有注册的变量都会被序列化。已注册但未定义的变量被标记为未定义。在之后的访问中这些变量也未被会话模块定义,除非用户以后定义它们。
有些类型的数据不能被序列化因此也就不能保存在会话中。包括 resource 变量或者有循环引用的对象(即某对象将一个指向自己的引用传递给另一个对象)。
Note: 会话处理是 PHP 4.0 添加的。
Note: 注意在使用会话时除非用 session_register() 函数注册了一个变量或者将一个新的键添加到了 $_SESSION 超全局数组中去,否则会话的记录不会被创建。不管会话是否用 session_start() 函数启动都是如此。
外部连接:» Session fixation。
会话模块不能保证存放在会话中的信息只能被创建该会话的用户看到。根据其存放的数据,还需要采取更多措施来主动保护会话的完整性。
评估会话中携带的数据并实施附加保护措施――这通常要付出代价,降低用户的方便程度。例如,如果要保护用户免于受简单的社交策略侵害(注:指在 URL 中显示的会话 ID 会被别人在电脑屏幕上看到,或被别的网站通过 HTTP Referer 得到等),则应该启用 session.use_only_cookies。此情形下,客户端必须无条件启用 cookie,否则会话就不工作。
有几种途径会将现有的会话 ID 泄露给第三方。泄露出的会话 ID 使第三方能够访问所有与指定 ID 相关联的资源。第一,URL 携带会话 ID。如果连接到外部站点,包含有会话 ID 的 URL 可能会被存在外部站点的 Referer 日志中。第二,较主动的攻击者可能会侦听网段的数据包。如果未加密,会话 ID 会以明文方式在网络中流过。对此的解决方式是在服务器上实施 SSL 并强制用户使用。
要编译本扩展模块无需外部库文件。
Note: 作为可选项,可以使用共享内存分配(mm),由 Ralf S. Engelschall 开发用于会话存储。必须下载 » mm 并安装。此选项在 Windows 平台下不可用。注意 mm 的会话存储模块不能保证到同一个会话的并发访问被正确地锁定。可能用基于共享内存的文件系统(例如 Solaris/Linux 中的 tmpfs,或 BSD 中的 /dev/md)来将会话存放到文件中更为恰当,因为这样会正确锁定。会话数据存放在内存中则 Web 服务器重启动会删除之。
会话的支持在 PHP 默认为激活。如果不想在 PHP 中加入会话支持,应在配置时指定 --disable-session 选项。要为会话存储使用共享内存分配(mm),配置 PHP 时指定 --with-mm[=DIR] 。
PHP 的 Windows 版本已经内置该扩展模块的支持。无需加载任何附加扩展库即可使用这些函数。
Note: 默认情况下,所有与特定会话相关的数据都被存储在由 INI 选项 session.save_path 指定的目录下的一个文件中。对每个会话会建立一个文件(不论是否有数据与该会话相关)。这是由于每打开一个会话即建立一个文件,不论是否有数据写入到该文件中。注意由于和文件系统协同工作的限制,此行为有个副作用,有可能造成用户定制的会话处理器(例如用数据库)丢失了未存储数据的会话。
这些函数的行为受 php.ini 的影响。
名称 | 默认值 | 可修改范围 | 更新纪录 |
---|---|---|---|
session.save_path | "" | PHP_INI_ALL | |
session.name | "PHPSESSID" | PHP_INI_ALL | |
session.save_handler | "files" | PHP_INI_ALL | |
session.auto_start | "0" | PHP_INI_ALL | |
session.gc_probability | "1" | PHP_INI_ALL | |
session.gc_divisor | "100" | PHP_INI_ALL | 自 PHP 4.3.2 起可用。 |
session.gc_maxlifetime | "1440" | PHP_INI_ALL | |
session.serialize_handler | "php" | PHP_INI_ALL | |
session.cookie_lifetime | "0" | PHP_INI_ALL | |
session.cookie_path | "/" | PHP_INI_ALL | |
session.cookie_domain | "" | PHP_INI_ALL | |
session.cookie_secure | "" | PHP_INI_ALL | 自 PHP 4.0.4 起可用。 |
session.use_cookies | "1" | PHP_INI_ALL | |
session.use_only_cookies | "1" | PHP_INI_ALL | 自 PHP 4.3.0 起可用。 |
session.referer_check | "" | PHP_INI_ALL | |
session.entropy_file | "" | PHP_INI_ALL | |
session.entropy_length | "0" | PHP_INI_ALL | |
session.cache_limiter | "nocache" | PHP_INI_ALL | |
session.cache_expire | "180" | PHP_INI_ALL | |
session.use_trans_sid | "0" | PHP_INI_ALL | 在 PHP <= 4.2.3 是 PHP_INI_ALL,在 PHP < 5 是 PHP_INI_PERDIR。自 PHP 4.0.3 起可用。 |
session.bug_compat_42 | "1" | PHP_INI_ALL | 自 PHP 4.3.0 起可用。 |
session.bug_compat_warn | "1" | PHP_INI_ALL | 自 PHP 4.3.0 起可用。 |
session.hash_function | "0" | PHP_INI_ALL | 自 PHP 5.0.0 起可用。 |
session.hash_bits_per_character | "4" | PHP_INI_ALL | 自 PHP 5.0.0 起可用。 |
url_rewriter.tags | "a=href,area=href,frame=src,form=,fieldset=" | PHP_INI_ALL | 自 PHP 4.0.4 起可用。 |
会话管理系统支持许多配置选项,可以在自己的 php.ini 文件中设定。这里只是个简短的概览。
此指令还有一个可选的 N 参数来决定会话文件分布的目录深度。例如,设定为 '5;/tmp' 将使创建的会话文件和路径类似于 /tmp/4/b/1/e/3/sess_4b1e384ad74619bd212e236e52a5a174If。要使用 N 参数,必须在使用前先创建好这些目录。在 ext/session 目录下有个小的 shell 脚本名叫 mod_files.sh 可以用来做这件事。此外注意如果使用了 N 参数并且 N 大于 0,那么将不会执行自动垃圾回收,更多信息见 php.ini。另外如果用了 N 参数,要确保将 session.save_path 的值用双引号 "quotes" 括起来,因为分隔符分号( ;)在 php.ini 中也是注释符号。
如果将此设定为一个全局可读的目录,例如 /tmp(默认值),服务器上的其他用户有可能通过该目录的文件列表破解会话。
Note: 在 PHP 4.3.6 之前,Windows 用户必须修改此选项以使用 PHP 的会话函数。必须指定一个合法路径,例如:c:/temp。
Note: 如果不同的脚本具有不同的 session.gc_maxlifetime 数值但是共享了同一个地方存储会话数据,则具有最小数值的脚本会清理数据。此情况下,与 session.save_path 一起使用本指令。
Note: 如果使用默认的基于文件的会话处理器,则文件系统必须保持跟踪访问时间(atime)。Windows FAT 文件系统不行,因此如果必须使用 FAT 文件系统或者其他不能跟踪 atime 的文件系统,那就不得不想别的办法来处理会话数据的垃圾回收。自 PHP 4.2.3 起用 mtime(修改时间)来代替了 atime。因此对于不能跟踪 atime 的文件系统也没问题了。
Note: 对于 PHP 4.1.2 或以下版本,可以通过加入 --enable-trans-sid 配置选项去编译来启用,从 PHP 4.2.0 起,trans-sid 特性总是被编译。 基于 URL 的会话管理比基于 cookie 的会话管理有更多安全风险。例如用户有可能通过 email 将一个包含有效的会话 ID 的 URL 发给他的朋友,或者用户总是有可能在收藏夹中存有一个包含会话 ID 的 URL 来以同样的会话 ID 去访问站点。
Note: 这是 PHP 5 引进的。
Note: 这是 PHP 5 引进的。
Note: 如果要符合 XHTML,去掉 form 项并在表单字段前后加上 <fieldset> 标记。
track_vars 和 register_globals 配置选项影响到会话变量是怎样存储和恢复的。
Note: 自 PHP 4.0.3 起,track_vars 总是打开的。
本扩展模块未定义任何资源类型。
以下常量由本扩展模块定义,因此只有在本扩展模块被编译到 PHP 中,或者在运行时被动态加载后才有效。
Note: 自 PHP 4.1.0 起,$_SESSION 如同 $_POST,$_GET,$_REQUEST 等一样成为全局数组。与 $HTTP_SESSION_VARS 不同,$_SESSION 总是具有全局范围。因此不要对 $_SESSION 使用 global 关键字。注意本文档已被改为在所有地方都使用 $_SESSION。如果倾向后者,可以将 $HTTP_SESSION_VARS 都替换成 $_SESSION。此外注意必须在使用 $_SESSION 之前先用 session_start() 启动会话。
在 $_SESSION 关联数组中的键名具有和 PHP 中普通变量名相同的规则,即不能以数字开头,必须以字母或下划线开头。更多细节见本手册中变量一章。
如果 register_globals 被禁用,则只有全局关联数组 $_SESSION 中的成员可以被注册为会话变量。被恢复的会话变量也只存在于 $_SESSION 数组中。
为提高安全性和代码的可读性,建议使用 $_SESSION(或在 PHP 4.0.6 或更低版本中用 $HTTP_SESSION_VARS)。使用了 $_SESSION,就没有必要使用 session_register(),session_unregister(),session_is_registered() 函数。访问会话变量就和其它变量一样。
Example#1 用 $_SESSION 注册变量
<?php
session_start();
// Use $HTTP_SESSION_VARS with PHP 4.0.6 or less
if (!isset($_SESSION['count'])) {
$_SESSION['count'] = 0;
} else {
$_SESSION['count']++;
}
?>
Example#2 用 $_SESSION 取消注册变量并且禁用了 register_globals
<?php
session_start();
// Use $HTTP_SESSION_VARS with PHP 4.0.6 or less
unset($_SESSION['count']);
?>
不要用 unset($_SESSION) 取消了整个 $_SESSION 数组,这样将不能再通过 $_SESSION 超全局数组注册变量了。
不能在会话变量中用引用,因为没有可行的方法将引用恢复到另一个变量去。
如果启用了 register_globals,则每个全局变量都能被注册为会话变量。在会话重新启动时,这些变量会被恢复到相应的全局变量中去。因为 PHP 必须知道哪些全局变量被注册为会话变量,用户需要用 session_register() 函数来注册变量。可以简单地通过在 $_SESSION 中设定变量来免去这样做。
在 PHP 4.3 之前,如果使用了 $_SESSION 并且仅用了 register_globals,则不要使用 session_register(),session_is_registered() 或 session_unregister()。出于安全及性能原因,建议禁用 register_globals。
如果启用了 register_globals,则全局变量和 $_SESSION 中的条目自动指向之前注册的同一个会话实例。不过如果变量是用 $_SESSION 注册的,则全局变量自下一个请求起才可用。
在 PHP 4.2.3 和之前版本中有个缺陷。如果用 session_register() 注册了一个新的会话变量,则在全局变量范围中的条目和 $_SESSION 中的条目在下一个 session_start() 之前没有引用到同一个值。即如果修改一个新注册的全局变量,不会在 $_SESSION 条目中反应出来。这在 PHP 4.3 中已被修正。
有两种方法传递一个会话 ID:
会话模块支持这两种方法。cookie 更优化,但由于不总是可用,也提供替代的方法。第二种方法直接将会话 ID 嵌入到 URL 中间去。
PHP 可以透明地转换连接。除非是使用 PHP 4.2 或更新版本,需要手工在编译 PHP 时激活。在 Unix 下,用 --enable-trans-sid 配置选项。如果此配置选项和运行时选项 session.use_trans_sid 都被激活,相对 URI 将被自动修改为包含会话 ID。
Note: php.ini 指令 arg_separator.output 允许定制参数分隔符。为完全符合 XHTML,这里用 &。
此外,可以用常量 SID,在会话启动时被定义。如果客户端没有发送适当的会话 cookie 的话,则 SID 的格式为 session_name=session_id,否则就为一个空字符串。因此可以无条件将其嵌入到 URL 中去。
下面例子演示了怎样注册一个变量,以及怎样用 SID 正确连接到另一个页面。
Example#3 对单一用户进行页面点击计数
<?php
if (!session_is_registered('count')) {
session_register('count');
$count = 1;
} else {
$count++;
}
?>
<p>
Hello visitor, you have seen this page <?php echo $count; ?> times.
</p>
<p>
To continue, <a href="nextpage.php?<?php echo strip_tags(SID); ?>">click
here</a>.
</p>
用 strip_tags() 来输出 SID 以避免 XSS 相关的攻击。
如果编译 PHP 时指定了 --enable-trans-sid,就不需要像上例那样输出 SID 了。
Note: 非相对的 URL 被假定为指向外部站点,因此没有附加 SID,因为这可能是个安全隐患将 SID 泄露给不同的服务器。
要实现数据库存储或其它储存方法,需要用 session_set_save_handler() 来创建一组用户级别的存储函数。