CXLIX. Session 会话处理函数

简介

PHP 中的会话支持由一种将特定数据保留用于之后的请求的方法组成。这样可以使用户建立更灵活的应用并提高网站的吸引力。

访问网站的来客会被分配一个唯一的标识符,即所谓的会话 ID。它要么存放在客户端的 cookie,要么经由 URL 传递。

会话支持允许用户注册任意数目的变量并保留给各个请求使用。当来客访问网站时,PHP 会自动(如果 session.auto_start 被设为 1)或在用户请求时(由 session_start() 明确调用或 session_register() 暗中调用)检查请求中是否发送了特定的会话 ID。如果是,则之前保存的环境就被重建。

小心

如果确实启用了 session.auto_start,则不能将对象放入会话中,因为类定义必须在启动会话之前加载以在会话中重建对象。

请求结束后所有注册的变量都会被序列化。已注册但未定义的变量被标记为未定义。在之后的访问中这些变量也未被会话模块定义,除非用户以后定义它们。

警告

有些类型的数据不能被序列化因此也就不能保存在会话中。包括 resource 变量或者有循环引用的对象(即某对象将一个指向自己的引用传递给另一个对象)。

注意: 会话处理是 PHP 4.0 添加的。

注意: 注意在使用会话时除非用 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 并强制用户使用。

需求

要编译本扩展模块无需外部库文件。

注意: 作为可选项,可以使用共享内存分配(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 版本已经内置该扩展模块的支持。无需加载任何附加扩展库即可使用这些函数。

注意: 默认情况下,所有与特定会话相关的数据都被存储在由 INI 选项 session.save_path 指定的目录下的一个文件中。对每个会话会建立一个文件(不论是否有数据与该会话相关)。这是由于每打开一个会话即建立一个文件,不论是否有数据写入到该文件中。注意由于和文件系统协同工作的限制,此行为有个副作用,有可能造成用户定制的会话处理器(例如用数据库)丢失了未存储数据的会话。

运行时配置

这些函数的行为受 php.ini 的影响。

表 1. 会话配置选项

名称默认值可修改范围更新纪录
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_* 常量进一步的细节与定义参见附录 H

会话管理系统支持许多配置选项,可以在自己的 php.ini 文件中设定。这里只是个简短的概览。

session.save_handler string

session.save_handler 定义了来存储和获取与会话关联的数据的处理器的名字。默认为 files。参见 session_set_save_handler()

session.save_path string

session.save_path 定义了传递给存储处理器的参数。如果选择了默认的 files 文件处理器,则此值是创建文件的路径。默认为 /tmp。参见 session_save_path()

此指令还有一个可选的 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(默认值),服务器上的其他用户有可能通过该目录的文件列表破解会话。

注意: 在 PHP 4.3.6 之前,Windows 用户必须修改此选项以使用 PHP 的会话函数。必须指定一个合法路径,例如:c:/temp

session.name string

session.name 指定会话名以用做 cookie 的名字。只能由字母数字组成,默认为 PHPSESSID。参见 session_name()

session.auto_start boolean

session.auto_start 指定会话模块是否在请求开始时自动启动一个会话。默认为 0(不启动)。

session.serialize_handler string

session.serialize_handler 定义用来序列化/解序列化的处理器名字。当前支持 PHP 内部格式(名为 php)和 WDDX(名为 wddx)。如果 PHP 编译时加入了 WDDX 支持,则只能用 WDDX。默认为 php

session.gc_probability integer

session.gc_probabilitysession.gc_divisor 合起来用来管理 gc(garbage collection 垃圾回收)进程启动的概率。默认为 1。详见 session.gc_divisor

session.gc_divisor integer

session.gc_divisorsession.gc_probability 合起来定义了在每个会话初始化时启动 gc(garbage collection 垃圾回收)进程的概率。此概率用 gc_probability/gc_divisor 计算得来。例如 1/100 意味着在每个请求中有 1% 的概率启动 gc 进程。session.gc_divisor 默认为 100

session.gc_maxlifetime integer

session.gc_maxlifetime 指定过了多少秒之后数据就会被视为“垃圾”并被清除。

注意: 如果不同的脚本具有不同的 session.gc_maxlifetime 数值但是共享了同一个地方存储会话数据,则具有最小数值的脚本会清理数据。此情况下,与 session.save_path 一起使用本指令。

注意: 如果使用默认的基于文件的会话处理器,则文件系统必须保持跟踪访问时间(atime)。Windows FAT 文件系统不行,因此如果必须使用 FAT 文件系统或者其他不能跟踪 atime 的文件系统,那就不得不想别的办法来处理会话数据的垃圾回收。自 PHP 4.2.3 起用 mtime(修改时间)来代替了 atime。因此对于不能跟踪 atime 的文件系统也没问题了。

session.referer_check string

session.referer_check 包含有用来检查每个 HTTP Referer 的子串。如果客户端发送了 Referer 信息但是在其中并未找到该子串,则嵌入的会话 ID 会被标记为无效。默认为空字符串。

session.entropy_file string

session.entropy_file 给出了一个到外部资源(文件)的路径,该资源将在会话 ID 创建进程中被用作附加的熵值资源。例如在许多 Unix 系统下都可以用 /dev/random/dev/urandom

session.entropy_length integer

session.entropy_length 指定了从上面的文件中读取的字节数。默认为 0(禁用)。

session.use_cookies boolean

session.use_cookies 指定是否在客户端用 cookie 来存放会话 ID。默认为 1(启用)。

session.use_only_cookies boolean

session.use_only_cookies 指定是否在客户端仅仅使用 cookie 来存放会话 ID。。启用此设定可以防止有关通过 URL 传递会话 ID 的攻击。此设定是 PHP 4.3.0 添加的。

session.cookie_lifetime integer

session.cookie_lifetime 以秒数指定了发送到浏览器的 cookie 的生命周期。值为 0 表示“直到关闭浏览器”。默认为 0。参见 session_get_cookie_params()session_set_cookie_params()

session.cookie_path string

session.cookie_path 指定了要设定会话 cookie 的路径。默认为 /。参见 session_get_cookie_params()session_set_cookie_params()

session.cookie_domain string

session.cookie_domain 指定了要设定会话 cookie 的域名。默认为无,表示根据 cookie 规范产生 cookie 的主机名。参见 session_get_cookie_params()session_set_cookie_params()

session.cookie_secure boolean

session.cookie_secure 指定是否仅通过安全连接发送 cookie。默认为 off。此设定是 PHP 4.0.4 添加的。参见 session_get_cookie_params()session_set_cookie_params()

session.cache_limiter string

session.cache_limiter 指定会话页面所使用的缓冲控制方法(none/nocache/private/private_no_expire/public)。默认为 nocache。参见 session_cache_limiter()

session.cache_expire integer

session.cache_expire 以分钟数指定缓冲的会话页面的存活期,此设定对 nocache 缓冲控制方法无效。默认为 180。参见 session_cache_expire()

session.use_trans_sid boolean

session.use_trans_sid 指定是否启用透明 SID 支持。默认为 0(禁用)。

注意: 对于 PHP 4.1.2 或以下版本,可以通过加入 --enable-trans-sid 配置选项去编译来启用,从 PHP 4.2.0 起,trans-sid 特性总是被编译。

基于 URL 的会话管理比基于 cookie 的会话管理有更多安全风险。例如用户有可能通过 email 将一个包含有效的会话 ID 的 URL 发给他的朋友,或者用户总是有可能在收藏夹中存有一个包含会话 ID 的 URL 来以同样的会话 ID 去访问站点。

session.bug_compat_42 boolean

PHP 4.2.3 以及更低版本有一个未公开的特性/错误,它允许用户在 register_globals 被禁用的情况下在全局范围内初始化一个会话变量。PHP 4.3.0 及更高版本会在使用此特性时并且启用了 session.bug_compat_warn 时发出警告。此特性/错误可以通过关闭此选项而禁用。

session.bug_compat_warn boolean

PHP 4.2.3 以及更低版本有一个未公开的特性/错误,它允许用户在 register_globals 被禁用的情况下在全局范围内初始化一个会话变量。PHP 4.3.0 及更高版本会在使用此特性时并且同时启用了 session.bug_compat_42session.bug_compat_warn 时发出警告。

session.hash_function integer

session.hash_function 允许用户指定生成会话 ID 的散列算法。'0' 表示 MD5(128 位),'1' 表示 SHA-1(160 位)。

注意: 这是 PHP 5 引进的。

session.hash_bits_per_character integer

session.hash_bits_per_character 允许用户定义将二进制散列数据转换为可读的格式时每个字符存放多少个比特。可能值为 '4'(0-9,a-f),'5'(0-9,a-v),以及 '6'(0-9,a-z,A-Z,"-",",")。

注意: 这是 PHP 5 引进的。

url_rewriter.tags string

url_rewriter.tags 指定在使用透明 SID 支持时哪些 HTML 标记会被修改以加入会话 ID。默认为 a=href,area=href,frame=src,input=src,form=fakeentry,fieldset=

注意: 如果要符合 XHTML,去掉 form 项并在表单字段前后加上 <fieldset> 标记。

track_varsregister_globals 配置选项影响到会话变量是怎样存储和恢复的。

注意: 自 PHP 4.0.3 起,track_vars 总是打开的。

资源类型

本扩展模块未定义任何资源类型。

预定义常量

以下常量由本扩展模块定义,因此只有在本扩展模块被编译到 PHP 中,或者在运行时被动态加载后才有效。

SID (string)

包含着会话名以及会话 ID 的常量,格式为 "name=ID",或者如果会话 ID 已经在适当的会话 cookie 中设定时则为空字符串。

范例

注意: 自 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() 函数。访问会话变量就和其它变量一样。

例 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']++;
}
?>

例 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

有两种方法传递一个会话 ID:

  • cookie

  • URL 参数

会话模块支持这两种方法。cookie 更优化,但由于不总是可用,也提供替代的方法。第二种方法直接将会话 ID 嵌入到 URL 中间去。

PHP 可以透明地转换连接。除非是使用 PHP 4.2 或更新版本,需要手工在编译 PHP 时激活。在 Unix 下,用 --enable-trans-sid 配置选项。如果此配置选项和运行时选项 session.use_trans_sid 都被激活,相对 URI 将被自动修改为包含会话 ID。

注意: php.ini 指令 arg_separator.output 允许定制参数分隔符。为完全符合 XHTML,这里用 &amp;。

此外,可以用常量 SID,在会话启动时被定义。如果客户端没有发送适当的会话 cookie 的话,则 SID 的格式为 session_name=session_id,否则就为一个空字符串。因此可以无条件将其嵌入到 URL 中去。

下面例子演示了怎样注册一个变量,以及怎样用 SID 正确连接到另一个页面。

例 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 了。

注意: 非相对的 URL 被假定为指向外部站点,因此没有附加 SID,因为这可能是个安全隐患将 SID 泄露给不同的服务器。

定制会话处理器

要实现数据库存储或其它储存方法,需要用 session_set_save_handler() 来创建一组用户级别的存储函数。

目录
session_cache_expire -- Return current cache expire
session_cache_limiter -- Get and/or set the current cache limiter
session_commit -- session_write_close() 的别名
session_decode -- Decodes session data from a string
session_destroy -- Destroys all data registered to a session
session_encode -- 将当前会话数据编码为一个字符串
session_get_cookie_params --  Get the session cookie parameters
session_id -- Get and/or set the current session id
session_is_registered --  Find out whether a global variable is registered in a session
session_module_name -- Get and/or set the current session module
session_name -- Get and/or set the current session name
session_regenerate_id --  Update the current session id with a newly generated one
session_register --  Register one or more global variables with the current session
session_save_path -- Get and/or set the current session save path
session_set_cookie_params --  Set the session cookie parameters
session_set_save_handler --  Sets user-level session storage functions
session_start -- Initialize session data
session_unregister --  Unregister a global variable from the current session
session_unset --  Free all session variables
session_write_close -- Write session data and end session