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

add a note add a note User Contributed Notes
nn162077php at smallcue dot com
11-Aug-2007 11:20
Bruno's posting of mod_files works for default (16) or 32, but it doesn't work for 64.    The following isn't great code since I'm not a great shell coder, but it does work better.  It replaces the middle section of Bruno's script

hash_chars="0 1 2 3 4 5 6 7 8 9 a b c d e f"
if test "$3" ; then
  if test "$3" -eq "32"; then
     hash_chars="$hash_chars g h i j k l m n o p q r s t u v"
  fi
  if test "$3" -eq "64"; then
     hash_chars="$hash_chars g h i j k l m n o p q r s t u v"
   hash_chars="$hash_chars w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z - ,"
  fi
fi

His posting should be updated (and mine should be removed).  :-)
Nigel Barlass
28-Jun-2007 01:07
Lima's note on sessions and browser's tabs needs to be modified for my version of php as the call to uniqid('') will return an alphanumeric string.
Hence the ereg statement should be:
if(!ereg('^SESS[0-9a-z]+$',$_REQUEST['SESSION_NAME'])) {...
spam at chovy dot com
02-Jun-2007 07:01
Regarding xhtml validation errors with w3c validator:

The problem is the validator doesn't support cookies, so our session_start() mechanism injects hidden input fields just below all form tags (and in urls).

You need to do two things to pass validation:

1) use fieldsets around *all* input fields (for xhtml 1.0+) to pass validation
2) then, before session_start() change the url_rewriter.tags list of tags to drop form and add fieldset:

   ini_set("url_rewriter.tags", "a=href,area=href,frame=src,fieldset=");
   session_start();

You can also set url_rewriter.tags anywhere, (see appendix for php.ini)
legolas558 d0t users dot sf dot net
01-May-2007 03:45
To clear out a possible doubt of other readers which have read this part of "Eric dot Deplagne at nerim dot net" note:

--------
In fact, two other variables (at least) play a role in session duration, and will explain that the session might last more than expected. The gc erasing your data is a probabilistic thing. Each time a session is opened, the probability the gc is started is session.gc_probability/session.gc_divisor. session.gc_probability defaults to 1 and session.gc_divisor defaults to 100, which makes a 1% probability.

This is to have sessions long enough. I'm unsure about having sessions lasting exactly the time we want them to.
--------

The gc erasing of the sessions is NOT a probabilistic thing, they are erased ONLY when they are older than the number of seconds specified in "session.gc_maxlifetime" ini setting.

The probabilistic fact is that old sessions will not be erased if the garbage collector is not run frenquently enough, but anyway it is your choice to have a higher or lower "session.gc_probability" on "session.gc_divisor" ratio.

So, talking from the server side point of view, a session can last more than the specified amount of time but not less. And if the client respects the cookies duration (in case of cookie-based sessions) those invalid sessions will not last more than expected.

You finally might want to add a timestamp inside the session and validate its age through it to radically solve the problem, if necessary. Example:

<?php

session_start
();

if (!isset(
$_SESSION['generated']))
  
$_SESSION['generated'] = time();
else {
// if the session is not allowed to live more, regenerate it
  
if (time() - $_SESSION['generated'] > ini_get('session.gc_maxlifetime'))
      
$_SESSION = array('generated' => time());
}
?>
g_s_b
27-Apr-2007 03:37
If you try to use your own session handling functions (eg. for saving session data to DB) make sure session.auto_start directive is 0.

The manual warns about not being able to put objects in your sessions with auto_start=1: I guess that for the same reason your customized handling functions are ignored is sessions start automatically.

Hope this saves somebody a few minutes.
skysama at googles_mail dot com
17-Apr-2007 06:56
If your session are not retrieving correctly make sure that session.cookie_secure is to set to 'Off' if you are NOT going through https. Everytime you navigate the site your session data will not be retrieved and your sessionid will change. It may be obvious but I spent two days trying to figuring this out. Hope it helps someone.
Edemilson Lima <pulstar at gmail dot com>
17-Apr-2007 01:17
Sessions and browser's tabs

May you have noticed when you open your website in two or more tabs in Firefox, Opera, IE 7.0 or use 'Control+N' in IE 6.0 to open a new window, it is using the same cookie or is passing the same session id, so the another tab is just a copy of the previous tab. What you do in one will affect the another and vice-versa. Even if you open Firefox again, it will use the same cookie of the previous session. But that is not what you need mostly of time, specially when you want to copy information from one place to another in your web application. This occurs because the default session name is "PHPSESSID" and all tabs will use it. There is a workaround and it rely only on changing the session's name.

Put these lines in the top of your main script (the script that call the subscripts) or on top of each script you have:

if(version_compare(phpversion(),'4.3.0')>=0) {
   if(!ereg('^SESS[0-9]+$',$_REQUEST['SESSION_NAME'])) {
       $_REQUEST['SESSION_NAME']='SESS'.uniqid('');
   }
   output_add_rewrite_var('SESSION_NAME',$_REQUEST['SESSION_NAME']);
   session_name($_REQUEST['SESSION_NAME']);
}

How it works:

First we compare if the PHP version is at least 4.3.0 (the function output_add_rewrite_var() is not available before this release).

After we check if the SESSION_NAME element in $_REQUEST array is a valid string in the format "SESSIONxxxxx", where xxxxx is an unique id, generated by the script. If SESSION_NAME is not valid (ie. not set yet), we set a value to it.

uniqid('') will generate an unique id for a new session name. It don't need to be too strong like uniqid(rand(),TRUE), because all security rely in the session id, not in the session name. We only need here a different id for each session we open. Even getmypid() is enough to be used for this, but I don't know if this may post a treat to the web server. I don't think so.

output_add_rewrite_var() will add automatically a pair of 'SESSION_NAME=SESSxxxxx' to each link and web form in your website. But to work properly, you will need to add it manually to any header('location') and Javascript code you have, like this:

header('location: script.php?'.session_name().'='.session_id()
     . '&SESSION_NAME='.session_name());

<input type="image" src="button.gif" onClick="javascript:open_popup('script.php?<?php
echo session_name(); ?>=<?php echo session_id(); ?>&SESSION_NAME=<?php echo session_name(); ?>')" />

The last function, session_name() will define the name of the actual session that the script will use.

So, every link, form, header() and Javascript code will forward the SESSION_NAME value to the next script and it will know which is the session it must use. If none is given, it will generate a new one (and so, create a new session to a new tab).

May you are asking why not use a cookie to pass the SESSION_NAME along with the session id instead. Well, the problem with cookie is that all tabs will share the same cookie to do it, and the sessions will mix anyway. Cookies will work partially if you set them in different paths and each cookie will be available in their own directories. But this will not make sessions in each tab completly separated from each other. Passing the session name through URL via GET and POST is the best way, I think.
Marcin Wiazowski
03-Apr-2007 08:28
In an example below, after session restart all session data will be lost without a workaround. It seems to be a PHP problem with a local variable $_SESSION_copy reference counter (fixed in PHP 5.2.1 - I think it has been fixed unintentionally during fixing a #40274 bug).

<?php // test.php

$_SESSION_copy = '';

function
test_restart_session()
{
//    global $_SESSION_copy; // Workaround 1: make $_SESSION_copy variable global (living after function return)

//    static $_SESSION_copy; // Workaround 2: make $_SESSION_copy variable static (living after function return)

  
session_destroy();
  
$_SESSION_copy = $_SESSION;
  
session_start(); // This function reads $_SESSION from session file (here: $_SESSION is emptied)
  
$_SESSION = $_SESSION_copy;

//    $_SESSION['workaround'] = 1; // Workaround 3: set/change any $_SESSION key INSIDE THIS FUNCTION
}

if (!isset(
$_GET['show_session_state']))
{
  
session_start();

  
test_restart_session();

  
$_SESSION['string_to_print'] = 'OK';

   echo
'Here $_SESSION always contains valid data: '.$_SESSION['string_to_print'];

   echo
'<br><br><a href="test.php?show_session_state=1">show session data during next script execution</a>';

  
// Without workaround $_SESSION data will be emptied after script finishes!
}
else
{
  
session_start();

   echo
'$_SESSION data during next script execution: '.$_SESSION['string_to_print'];

   echo
'<br><br><a href="test.php">test once again</a>';
}

exit;
?>
Marcin Wiazowski
03-Apr-2007 08:11
'session.cookie_domain' should be set to empty string for all local domain names, not only for 'localhost' (but should not be empty for local IP addresses):

<?php
ini_set
('session.cookie_domain', (strpos($_SERVER['HTTP_HOST'],'.') !== false) ? $_SERVER['HTTP_HOST'] : '');
?>
gordon_e_rouse at yahoo dot com dot au
29-Mar-2007 03:06
Multi-site security risk!

Suppose you use session security to determine login status, and you have several such sites on the webserver which essentially assign a session token to determine your login status. A common occurence for webdeveloping companies that have a standard system or package, and are not using a high security setup. (ie all sites share the same session path directory)

PHP sends the browser a cookie and that cookie should only be valid for the site it was retrieved from, but only the browser determines this, the server does not record the path or domain with the session cookie, so technically you could modify the browsers cookie jar to send the same cookie request for a different domain or path than what was originally meant.

This means that if the same session variables are used to determine a logon status on different sites on this server, then the user can access all these sites with the one session id.

The solution is to write the domain and or path as session variables as well, thus when logged on status is checked, the script checks that the session was created through the current domain or path.
jphansen at uga dot edu
12-Mar-2007 09:06
I wanted to retain a session variable's value after a session_unset() and encountered this anomaly: PHP can assign your variable by reference/pointer if your data source is a session variable, even if "&" isn't supplied.

<?
$_SESSION
['x'] = "foo";
$x = $_SESSION['x'];
echo
$x; // "foo"
$_SESSION['x'] = "bar";
echo
$x; // "bar";
?>

To fix this, you can assign your variable to a new variable:

<?
$_SESSION
['x'] = "foo";
$x = $_SESSION['x'];
$y = $x;
echo
$y; // "foo"
$_SESSION['x'] = "bar";
echo
$y; // "foo";
?>
Parikesit
09-Feb-2007 06:26
localhost problem
-----------------
When using localhost and session.cookies not work, simply remove domain from session.cookie_domain.

We can use "session_set_cookie_params" function or ini_set to do this. Example:

$cookie_path = 'my_path';
$cookie_domain = 'localhost'; //or any valid domain

init_session_cookies($cookie_path, $cookie_domain);

//maybe we create new function for handling this
function init_session_cookies($path, $domain) {
  if ($domain=='localhost') $domain='';
  if (function_exists('session_set_cookie_params')) {
   session_set_cookie_params(0, $path, $domain);
  } else {
   ini_set('session.cookie_lifetime', '0');
   ini_set('session.cookie_path', $path);
   ini_set('session.cookie_domain', $domain);
  }
}

Best,
Parikesit (zae_lokamaya #at# yahoo #dot# co #dot# uk)
Jim Rogers at mail dot com
31-Jan-2007 04:57
url_rewriter.tags = "a=href,area=href,frame=src,fieldset="

Modifying url_rewriter.tags to the above line in the php.ini file gave me mixed results, including some pages adding the sessionid after BOTH the form tag and the fieldset tag.

Then I noticed the comment at the top of the php.ini file: "If you modify the file, you must copy it to all subdirectories that contain php."  This has to do with the search order php uses to find the php.ini file.  PHP looks in the current working directory first.

Works.
Dave Matlock
29-Jan-2007 10:35
In reference to the IE6 issue that results in loss of session data after a page refresh, I found a solution.  In my case, the host that was experiencing the issue contained an underscore (host_dev).  Apparently underscores are not valid in host names and IE is the only browser that seems to care.  I removed the underscore and all is well.  I found this information here: http://us2.php.net/manual/en/function.setcookie.php#67262
Stephen
02-Jan-2007 12:47
jmoore at sober dot dk wrote: "Note: Changing sessions to use a directory other than /tmp will be harder to find, but no more secure."

This is true. I used to think that this could be solved by storing sessions in a different folder that is accessible only by apache, but the reality is that assuming you are only running a single apache server (as any shared host will), every user can command apache to retrieve that data.

The best solution is to store session data in a database by writing (and informing PHP about) specific session handling functions that store the data using a private username and password that only you know. There is a good discussion of this in Essential PHP Security by Chris Shiflett (O'Reilly Books).
02-Jan-2007 11:44
Bruno's shell script below is very helpful for those who want to customize their session storage. I would only warn that there is a typo in his instructions. He states that the script should be called as:

./mod_files.sh ./mod_files.sh /tmp/session 3 32

But you should only type mod_files.sh (or whatever the name of your script file is) once, not twice. It doesn't need its own name as an argument.

Also - make sure you don't make the silly mistake I did of naming your script file "script" and then trying to call it with the argument:

script /tmp/session 3 32

...because "script" is the name of a real Unix function, which will launch a completely different function and then overwrite your original file with whatever you type after that. If you do that by accident, you will need to type "exit" to get out of the script recording mode, and then replace your script file. Also, you have to include the './' prior to the name (i.e. './mod_files.sh' instead of 'mod_files.sh') unless you are running the script from the folder where you installed it, because the location where you are installing this probably (hopefully!) isn't in your PATH shell variable.
Marce!
22-Dec-2006 08:20
Be careful when using use_trans_sid and javascript together. When adding a form dynamically with javascript, you usually need to put it in quotes and use addslashes() for your html code.
This will nicely put slashes into the code for your form and the javascript will work perfectly. Or so you would think. PHP does recognise the form, even with the slashes added. So it will neatly insert a hidden variable to your form, with the name of your session variable and the value of the session ID. This hidden variable isn't slashed though, and it may break your javascript code.

The only solution I came up with, is to manually add the hidden variable, before applying the addslashes() function. It will then be shown correctly, and PHP will not insert the hidden value. Of course you may get both a cookie and the variable, depending on your settings, but it does work.
bens at effortlessis dot com
13-Dec-2006 04:25
Note on using (the excellent!) Sharedance:

Sharedance as of 0.6 doesn't do any kind of locking. It works well for an application cluster (which I'm using) and allows concurrent writes, which means that the "last one to write wins" order stands. (which has virtually no effect on how I use sessions)
aserboni at gmail dot com
04-Dec-2006 08:59
The session variable is still lost when navigating even if I change $setting or $_SESSION['setting'].

If you assign a session subscript/key to the same name as a variable, the session variable will be volatile and lost upon navigating.

For example, if passing a setting that you want in $_SESSION, don't do this:

<?
$setting
= $_REQUEST['setting'];
if (!empty(
$setting))
  
$_SESSION['setting'] = $setting;
?>

Instead, rename $setting or $_SESSION['setting'].
01-Dec-2006 08:52
To Jestin S Larson :

Your generated ids arent safe, they are guessable because depending only on guessable vars. You must add a random var to your id (ie in md5( ... ) )
robin at amiance dot com
26-Nov-2006 06:19
If you want the simplest way there is to log a session out after 30 minutes (or any other period of inactivity) simply add a second line after session_start(), like this:

session_start();
setcookie("PHPSESSID",$_COOKIE['PHPSESSID'],time()+1800);

Where 1800 is the time in seconds before the session should expire. PHPSESSID is the default session ID. This way, every time a user loads a page they get their session extended.
klenwell at gmail dot com
06-Oct-2006 12:39
In response to marou at marou dot com responding to fondman at hotmail dot com regarding local variables overwriting SESSION variables:

I was encountering this same problem.  On my local PHP 5 server, no problem.  Remote host's server running PHP 4: major headaches.

Initially, I concluded it was some kind of bug in PHP 4 and older.  However, according to the devs, this is not a bug (see http://bugs.php.net/bug.php?id=36366).  Apparently, this is intended behavior when register_globals is set to ON. 

But, as noted elsewhere in the docs, register_globals cannot be turned off at runtime using ini_set().  However, you can use a local .htaccess or php.ini to override the setting.  This site helped me solve the problem:

http://www.nyphp.org/phundamentals/ini.php

Full details of my experience with this issue can be found here:

http://tinyurl.com/o6p7y
wings at 25th dot com
05-Oct-2006 01:59
Jestin's Session class is a nice alternative, but I had trouble with it.  The results of the filesize() function are cached.  I ended up having to put a call to clearstatcache() at the head of Session::_getdata();  Otherwise, your session data gets truncated.
jon at jonroig dot -----com
26-Sep-2006 11:30
This code is to solve a problem that's probably becoming more common as we move into the AJAX world -- how the &*^*&^ to access session data if you're transferring data back 'n' forth from a server and you don't have cookies to help you out.

The function accepts the session ID then grabs the session data straight from the directory, parses it, and returns it as an array.

<?
function grabSessionData($sessionInput)
  {
 
$sessionThing = "/tmp/sess_".$sessionInput;
 
$textContentsArray = file($sessionThing);
 
$serializedData = '';

  foreach (
$textContentsArray as $line => $textLine)
   {
  
$serializedData = $serializedData.$textLine;
   }
 
$sessionArray = explode(";",$serializedData);
 
$sessionDataArray = array();
  foreach (
$sessionArray as $line => $dataLine)
   {
  
$dataArray2 = explode('|',$dataLine);
  
$dataArray3 = explode(':',$dataLine);
  
$sessionDataArray[$dataArray2[0]] =
    
str_replace('"','',$dataArray3[count($dataArray3)-1]);
   }
  return
$sessionDataArray;
  }

// example
$sessionInfoArray = grabSessionData('f58c92b76c1dd553c56dd428ea7a1bfc');
?>
steffen at steffen-ille dot de
13-Sep-2006 12:02
old problem was:
internet explorer has security settings which reject your php session cookie within different framesets (third party cookies). one approach to handle this was to pass the session id to the other frames in the url. the www says, this is a little bit unsafe...

another trick to handle this is, to trick the explorer with the php header funktion. as described in the microsoft knowledgebase
article http://support.microsoft.com/kb/323752/EN-US/

you simply add a header at the top of your scripts _bevore_ the session_start() is called:

header('P3P: CP="CAO PSA OUR"');

and now test it and enjoy...
12-Sep-2006 06:28
Here is a replacement I wrote for Session Handeling.

Before use, you must replace "[--PATH-TO-SESSION-FOLDER--]" with the path to where sessions should be stored.  Make sure to set the proper permissions for that folder :)

--------------------------------------------------------------------

<?
 
class Session
 
{
  function
_makeid()
  {
  
$time    = time();
  
$ip      = $_SERVER["REMOTE_ADDR"];
  
$agent  = $_SERVER["HTTP_USER_AGENT"];
  
$md5 = md5($time.$ip.$agent);
  
setcookie("SClass", $md5);
   return(
$md5);
  }

  function
getid()
  {
  
$md5 = $_COOKIE['SClass'];
   return(
$md5);
  }

  function
start()
  {
  
$id = $this->getid();
   if (!
$id) { $id = $this->_makeid(); }
  }

  function
_getfile()
  {
  
$file = $this->getid();
  
$file .= ".ssn";
  
$file = "[--PATH-TO-SESSION-FOLDER--]".$file;
   return(
$file);
  }

  function
_setdata($data)
  {
  
$file = $this->_getfile();
  
$fs  = @fopen($file, w);
   if (
$fs) { fwrite($fs, $data); fclose($fs);}
   if (!
$fs) { return(FALSE); }
  }

  function
_getdata()
  {
  
$file = $this->_getfile();
  
$fs  = @fopen($file, 'r');
   if (
$fs) { $data = fread($fs, filesize($file)); fclose($fs); return $data; }
   if (!
$fs) { return(FALSE); }
  }

  function
set($key, $value)
  {
  
$data          = $this->_getdata();
  
$data          = base64_decode($data);
  
$array        = unserialize($data);
  
$array["$key"] = $value;
  
$newdata      = serialize($array);
  
$newdata      = base64_encode($newdata);
  
$this->_setdata($newdata);
  }

  function
get($key)
  {
  
$data = $this->_getdata();
  
$data = base64_decode($data);
  
$array = unserialize($data);
   return(
$array[$key]);
  }

 }
?>

--------------------------------------------------------------------
Useage:

 $Session = new Session();
 $Session->start();
 $Session->set($key, $value);
 $sessionvalue = $Session->get($key);

--------------------------------------------------------------------

- Jestin S Larson
nhap24
09-Sep-2006 12:03
I spent about 8 hours debugging a problem with sessions and redirecting, and I finally found the problem, which after looking back, should have been the first thing I tried.

I use sessions to prevent hotlinking, so if someone hotlinks my file, I will redirect them to a different page by re-writing the header information.  When I redirected, I sent path information in the GET data about the file they were trying to access.  Long story short, when I redirected with a forward-slash "/" AFTER the .php, the page would create a different session ID than the rest of my domain was using.  This happened despite the fact that the cookie path was set to "/" (which should have captured any path on my domain). 

The issue was even harder to figure out, because the page with the wrong session ID was NOT creating a second cookie.  The only cookie was the one with the proper ID, but the broken page did not use this cookie (where then, did it get this ID from?).

To fix the problem, I simply removed the forward-slash from the GET data (base64_encoding works nicely).

I imagine this is an issue with my browser parsing the url, but I tried both Opera and Firefox (IE doesn't load anymore :/) and both browsers showed the same problem.

I looked into hidden header data, failure to write the cookie, trans_ses_id (even though it was set to false), HTTP_HOST, everything, but in the end it was just a stupid forward-slash that had done me in.
greenthumb at 4point-webdesign dot de
01-Sep-2006 06:56
Similar to the use of captcha images you can easely track and advise user who don't accept cookies, especially no session cookies without redirecting them.

so here's the deal:

if the main script (which outputs the html) doesn't have a value in it's session which says that a session is running successfully a different value is saved in the session and an image is included in the html which checks wether the session-check value is set or not.

If it's set the image-script sets a confirming value which will verify the session to be running correctly and output a transparent 1*1px gif.

If the value is not set the image outputs an advising image which tells to allow cookies.

You can also wrap the image with a link who refers the user  to a page addressing the cookie issue. If the 1px trans is generated the user will hardly find the link, but if the error-image is generated he will surely be able to click it.

pros:
- works
- no rerouting, you can see the result on the first page opened by the user
- no javascript

cons:
- bad accessebility (if you give the image an alt-text any blind user will read it at least on the first call, but you cold also write this into the alt text... so maybe there are no cons)

i hope this'll help
in dot tray at NOSPAM dot tiscali dot co dot uk
24-Aug-2006 01:19
Some users turn off cookies on their browser. It would be nice to advise such users that they need to switch on cookies to use your web site, especially if you do not allow URL session IDs.

The first request to your site will not contain cookie data from the user. Some developers do a little trick on the server at this point to redirect the user to the same or different page and in so doing endeavour to deliver a cookie so that it can then be determined if this was successful on the second request. It has the advantage of letting the no-cookie user know at the on-set if there is a problem. Of course this adds an overhead of time and server resources. It also might impare your site's position with popular search engines.

So, since many sites will work to a point without sessions working (i.e. scatterings of static links and basic searches), I allow the user to load the first page and then perform the cookie check upon the user's subsequent request. If the cookie canot be found I place advice text accordingly 

simple (unchecked) solution...

function cookie_check() {
if (!isset($_COOKIE['PHPSESSID'])) {
return false;
} else {
return true;
}

if (!cookie_check()) {
echo "This site uses cookies, dah dah dah...";
}

I have looked fairly extensively for information on this issue but there isn't a great deal about. So, if anyone out there has more experience of this...
Bruno Negrao G Zica
24-Aug-2006 01:13
#! /bin/sh
# NAME
#      mod_files.sh  - Update of the php-source/ext/session/mod_files.sh
#
# SYNOPSIS
#      mod_files.sh basedir depth [numberofsubdirs]
#
# DESCRIPTION
#      this script creates the directories tree used by php to store the session files
#      (see php.ini - 'session.save_path' option)
#
#      Example: if you want php to store the session files in a directory tree
#      of 3 levels of depth containing 32 directories in each directory,
#      first, put the setting bellow in the php.ini file:
#
#      session.save_path = "3;/tmp/session"
#
#      Now create the basedir directory: 'mkdir /tmp/session'
#
#      Then, call this scrip with the following arguments:
#
#      ./mod_files.sh ./mod_files.sh /tmp/session 3 32

if test "$2" = ""; then
       echo "usage: $0 basedir depth [numberofsubdirs]"
       echo "numberofsubdirs: if unset, defaults to 16. if 32, 32 subdirs, if 64, 64 subdirs."
       exit 1
fi

if test "$2" = "0"; then
       exit 0
fi

hash_chars="0 1 2 3 4 5 6 7 8 9 a b c d e f"
if test "$3" -a "$3" -eq "32"; then
  hash_chars="$hash_chars g h i j k l m n o p q r s t u v"
  if test "$3" -eq "64"; then
   hash_chars="$hash_chars w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z - ,"
  fi
fi

for i in $hash_chars; do
       newpath="$1/$i"
       mkdir $newpath || exit 1
       sh $0 $newpath `expr $2 - 1` $3
done
entropicchild
24-Aug-2006 04:09
I just went looking through the /tmp dir on a shared hosting server, and noticed all the php sessions stored there (bad idea for shared hosting). I noticed that some of these people/scripts are storing plain-text passwords in their sessions (another very bad idea). Here's a hint people: do not store ANYTHING that may present a security risk in the session data! This includes, passwords, creditcard information, etc.

It is best, under ALL circumstances that a dir other than /tmp be used for session data. Btw, whomever it was that said it was no more secure to use a diff dir was wrong. The /tmp dir is a globally readable/writable dir.

If you are using shared hosting, then you better be sure your session data is being saved within your home dir and NOT web-accessible. If you are a shared hosting sysadmin, you should be doing this for your users automatically... as well as any other tmp files (file uploads, etc...).
http://mike.eire.ca/
09-Aug-2006 10:08
It should be noted that PHP makes the decision about whether SID is empty based on the HTTP request.  Specifically, if there is no cookie sent with the GET or POST request, SID is populated when the session is initiated.

What does this mean?  If you want your site to support clients who have cookies disabled, your links will have PHPSESSID appended to them on the page where the session is created.

It seems like the only way around this is a double redirect...
Eric dot Deplagne at nerim dot net
01-Aug-2006 10:34
One more note on session duration, and especially long sessions. I hope I've now understood the problem, and this may be useful.

The duration of a session is indeed controlled by many things (too many for my taste).

The first thing is session.cache_expire, which should be *the* thing, you can set it with ini_set(), or with session_cache_expire(). The value is in minutes, and defaults to 180, which is 3 hours and should be enough.
 
  <?php //ini_set("session.cache_expire","180"); // default is 180, which is 3 hours... ?>

But that does not work !

Second thing to take into account, session.gc_maxlifetime. Indeed, if the garbage collector kills your session data, you're lost. So we set it with ini_set. The value is in seconds, and defaults to 1440, which is only 24 minutes and might be a little short.

  <?php ini_set("session.gc_maxlifetime","3600"); // default is 1440, which is only 24 minutes ?>

Here I should have a 1 hour session. But in fact, I don't !

Third thing to take into account, session.save_path. If other people mess up with it, like if it's "/tmp" as it is by default, and their gc has not the same idea as yours, you're lost. So we set it with ini_set or session_save_path.

  <?php session_save_path("/my/own/path/without/url/sessions"); ?>

Here we are. As you can read in the example it should be a path you control (and your web server can write), with no access via URL.

Of course, all these parameters must be set before calling session_start().

In fact, two other variables (at least) play a role in session duration, and will explain that the session might last more than expected. The gc erasing your data is a probabilistic thing. Each time a session is opened, the probability the gc is started is session.gc_probability/session.gc_divisor. session.gc_probability defaults to 1 and session.gc_divisor defaults to 100, which makes a 1% probability.

This is to have sessions long enough. I'm unsure about having sessions lasting exactly the time we want them to.
rehevkor5 at fastmail dot fm
25-May-2006 06:34
If you have a value specified for session.referer_check you may run into difficulty when someone accesses your site and attempts to log in with a mis-capitalized URL.  The logon will fail because any calls to session_start() will result in the existing session being trashed and a new one being created.  This becomes a bigger problem when the logon is followed by a header("Location: ...") redirect, because the session_start() at the top of the page will fail.

Because session_start() always returns true, it's not obvious how to detect when the referer check fails.  To detect it, I have come up with a method which compares the intended session id with the session id after session_start() is run.  If they are different, the user is redirected to the proper location.  This example uses a technique to avoid session id fixation, as well.

ini_set('session.referer_check', 'www.yourdomain.edu/ltr/');
ini_set('session.use_only_cookies', 1);
session_name('yourapp'.str_replace('.', '', $_SERVER['REMOTE_ADDR']));
session_start();
if($_GET['badreferer'])
   echo 'You tried accessing the site with a bad URL. Try logging on again.';
if(isUserAuthed($name, $pass))
{
   $old_sessid = session_id(); //save current session id so we can delete it later
   if( !session_regenerate_id() ) //get a new session id (must do this before destroying the old session)
       die("Couldn't regenerate your session id.");
              
   $new_sessid = session_id(); //save new session id so we can get back to it
              
   session_id($old_sessid);
   unset($old_sessid);
   session_destroy(); //destroy the session they got before they logged in
              
   session_id($new_sessid);
   session_start(); //start the new session
              
   $_SESSION = array(); //not really necessary any more, but still a good idea
              
   if(session_id() != $new_sessid)
   {
       /*If this is true, then the session_start() failed to work properly. If session_start() failed to work properly, the most likely cause is that the referer url is different from that set in fuxebox.ini on session.referer_check. The most common cause of this is URL capitalization problems. Therefore, we relocate them to the proper URL, and set a flag to display an error because we can't use pushError() if the session isn't valid. */
                  
       $good_url = ini_get('session.referer_check');
       header('Location: http://'.$good_url.'?badreferer=1');
       exit;
   } else {
       unset($new_sessid);
       //Set session variables here
       $_SESSION['isloggedin'] = 1;
      
       echo 'You have been logged in.';
   }

}
rajasekhar_s7 at yahoo dot co dot in
19-May-2006 11:07
POST the variable can give the page Expire Warnig when you press the BACK button.
               Solution: Put this code on the page top.
           <?    session.cashe.limiter(private, must-revalidate?>
php AT coryforsyth [removeme] d0t com
09-May-2006 02:14
I just finished a marathon debugging session with sessions, and I wanted to share what I learned with the rest of the PHP community because this has been a problem I've battled on several projects before finally solving it today.

I am using sessions to register a user's ID, and every so often, for no apparent reason, the session would seem to expire and my logged-in user would get kicked out.

This is the code I am using to validate a user:
<?php
session_start
();

$auth = false;

if (isset(
$_SESSION['user_id'])) {
  
$user = new User($_SESSION['user_id']);
  
$auth = true;
else {
  
$login = $_REQUEST['login'];
  
$password = $_REQUEST['password'];
  
$login = clean($login);
  
$password = clean($password);
  
// clean() is my own function to escape quotes and so on
  
$user = checkLogin($login,$password);
   if (!
is_null($user)) {
      
$auth = true;
   }
}

if (!
$auth) {
   die(
"you must log in.");
}

// etc etc ... //
?>

I was scratching my head as to why, on about 10% of the times I sent a user to a new page (a PHP page that didn't have any of the sessions stuff--no session_start() and no mention of any session variables), when they came back the session variables were all empty.

I came upon a clue when I used ini_set to change the session.save_path location.  My idea was that maybe the session files in /tmp were being deleted by other users on the system or something.  So I added this line before session_start():
<?php
ini_set
('session.save_path',"/path/to/unique/dir/");
?>
I knew no one else was saving their sessions there, so I started logging in to my page and at the same time checking for the creation of new sess_* files.  I noticed that sometimes, especially when I had problems, a second session file (that was empty) would appear.

What I realized was that the URL was changing from www.coryforsyth.com to coryforsyth.com.  When the presence of the WWW changed, PHP thought it was a different session and created an empty one that caused my script to log me out.  If I went to the location bar of my browser and added/removed the WWW (to the way it had been before), all was well and I was still logged in.

An incredibly thorny problem. I hope this post helps someone else fix it, or even better prevent it.  I changed my hosting preferences to automatically add a WWW to my domain if it wasn't typed that way.

thanks,
Cory Forsyth
brady at volchok dot com
17-Apr-2006 07:15
Session locking (concurrency) notes:

As mentioned several times throughout this section on Sessions, the default PHP session model locks a session until the page has finished loading. So if you have two or three frames that load, and each one uses sessions, they will load one at a time. This is so that only one PHP execution context has write access to the session at any one time.

Some people work around this by calling session_write_close() as soon as they've finished writing any data to the $_SESSION - they can continue to read data even after they've called it. The disadvantage to session_write_close() is that your code still will lock on that first call to session_start() on any session'ed page, and that you have to sprinkle session_write_close() everywhere you use sessions, as soon as you can. This is still a very good method, but if your Session access follows some particular patterns, you may have another way which requires less modification of your code.

The idea is that if your session code <b>mostly</b> reads from sessions, and rarely writes to them, then you can allow concurrent access. To prevent completely corrupted session data, we will lock the session's backing store (tmp files usually) while we write to them. This means the session is only locked for the brief instant that we are writing to the backing store. However, this means that if you have two pages loading simultaneously, and both modify the session, the <i>Last One Wins</i>. Whichever one loads first will get its data overwritten by the one that loads second. If this is okay with you, you may continue - otherwise, use the session_write_close method, above.

If you have complicated bits of code that depend on some state in the session, and some state in a database or text file, or something else - again, you may not want to use this method. When you have two simultaneous pages running, you might find that one page runs halfway through, modifying your text file, then the second one runs all the way through, further modifying your text file, then the first one finishes - and your data might be mangled, or completely lost.

So if you're prepared to debug potentially very, very nasty race conditions, and your access patterns for your sessions is read-mostly and write-rarely (and not write-dearly), then you can try the following system.

Copy the example from session_set_save_handler() into your include file, above where you start your sessions. Modify the session write() method:

<?
function write($id, $sess_data)
{
  global
$sess_save_path, $sess_session_name;

 
$sess_file = "$sess_save_path/sess_$id";
  if (
$fp = @fopen($sess_file, "w")) {
  
flock($fp,LOCK_EX);
  
$results=fwrite($fp, $sess_data);
  
flock($fp,LOCK_UN);
   return(
$results);
  } else {
   return(
false);
  }

}
?>

You will probably also want to add a GC (Garbage Collection) method for the sessions, as well.

And of course, take this advice with a grain of salt - We currently have it running on our testing server, and it seems to work OK there, but people have reported terrible problems with the Shared Memory session handler, and this method may be as unsafe as that.

You can also consider implementing your own locks for scary concurrency-sensitive bits of your code.
marcosdsanchez {a t} gmail [_dot_] com
08-Apr-2006 02:52
********************WARNING***********************

There's a bug in Internet explorer in which sessions do not work if the name of the server is not a valid name. For example...if your server is called web_server (_ isn't a valid character), if you call a page which uses sessions like http://web_server/example.php your sessions won't work but sessions will work if you call the script like this

[IP NUMBER]/example.php

Took me a lot of time to find out why my PHP sessions worked perfectly in Firefox and Opera but they didn't work in internet explorer

***************************************************
Cosmo
01-Apr-2006 12:47
Fairly new to PHP, I've been looking to alter session timeouts on a shared host where I don't have direct access to configure php.ini.  There doesn't appear to be any easy way to find out how to do this in this manual (nor from a quick web search).

The code below seems to work OK to set session timeout.    a timeout of 30 secs is used for convenient testing.  gc settings must come before session_start().

The garbage collection is made 100% by setting probability and divisor to the same value - if I have correctly understood what these functions do.
 
(on the first pass of the file, there is no session file - that's established only when the script ends for the first time.  Keep reloading to test).

Comments welcome.

<?php
  ini_set
('session.gc_maxlifetime',30);
 
ini_set('session.gc_probability',1);
 
ini_set('session.gc_divisor',1);
  
session_start();
  
// check to see what's happening
$filepath = ini_get('session.save_path').'/sess_'.session_id();
  
if(
file_exists($filepath))
{
  
$filetime = filemtime ($filepath);
  
$timediff = mktime() - $filetime;
   echo
'session established '.$timediff.' seconds ago<br><br>';
}
?>
Josh
09-Mar-2006 06:35
I had users of my site occsaionally complaining that they were being logged out after short times of inactivity (usually when they were creating content to send to the site). I had a high cookie timout so I couldn't understand why this was happening, and I never managed to replicate the behaviour myself.

I've just come accross the "gc_maxlifetime" property. This seems to be the culprit; it's set to only 24 minutes! The worst part is that it combines with the "gc_probability" property to produce unpredictable results. I'm mysified as to why the time limit is so low by default, but hopefully increasing it will fix up my errors.
crown2gain at yahoo dot com
01-Mar-2006 06:19
I just spent a lot of time trying to figure out why my session variables were not available after I seemed to have set them(could echo after setting).  I use the same script for several different functions, so the user may reload the page for other purposes.  Someone else posted the use of session_write_close();  before a Location redirect.  This also worked in put this after I set the session variables the variables are available when the page reloads for another function.

$_SESSION['guid'] = $guid;
$_SESSION['userdata'] = $response;
session_write_close();
a l bell at hutchison dot com dot au
28-Feb-2006 11:17
Please Note;

Internet explorer users beware.

When using session_start() to begin a session this session will remain open until the page has finished loading or it is explicitly terminated.

You can lose the session however if the the page contains a reference to <img src=""> with name and id references (which may be used if the image is referencing a dynamic image, called by javascript)  This seems to casue IE6 to refresh the page session id and hence loose the session.

This took hours for me to diagnose when users were getting unexpectedly logged out of my site due to this "" in the img src.
kintar at infinities-within dot net
28-Feb-2006 05:10
Important note that it just took me the better part of two hours to figure out:  Even if you're using session_write_close(), calling exit after a redirect will eat your session variables.  I had the following:

Source of register.php:
<?PHP

// Some files included here

// Process our posted form data
$result = processPost();

if (
$result)
{
 
redirect('success.php');
}
else
{
 
redirect('failure.php');
}

exit;
?>

processPost() was setting a couple of session variables, including an error message, but neither results page was seeing those variables.  I removed the exit call from the register page, and all works fine.

/bonks self
phpkloss at wholewheatradio dot org
22-Feb-2006 08:57
If you're having trouble with unset($_SESSION[$something]) working, here's what I discovered (Win2K/PHP 5.x.x).  Assume you want to step through $_SESSION and delete (i.e. unset) certain elements.  So,

foreach ($_SESSION as $key=>$value) {
   if (stristr($key,'something_to_delete')) {
     echo "Will unset $key that has value of $value";
     unset($_SESSION[$key]);
}

What I found was that although the $_SESSION elements were deleted in memory, the actual session file was not being written out with the changes (even after using session_write_close).  What fixed it for me was to COPY $_SESSION INTO A DUMMY ARRAY, THEN STEP THROUGH THAT DUMMY ARRAY TO FIGURE OUT WHICH ELEMENTS OF THE REAL $_SESSION TO DELETE.  I.e.

foreach($_SESSION as $key=>$value) {
  $dummy[$key]=$value;  // copy to a dummy array
}

foreach ($dummy as $key=>$value) {
   if (stristr($key,'something_to_delete')) {
     echo "Will unset $key that has value of $value";
     unset($_SESSION[$key]);
}

It appears that as you're stepping through the actual $_SESSION array in a foreach, if you unset elements of it, those changes won't be recorded to the session disk file.

'Course then again, my coffee is running low so I could be wrong.  But hopefully it's something others can try who might be having a similar problem.
20-Feb-2006 06:21
In response to "djohnson at jsatech dot com", posted 09-Dec-2005 09:00

"Be warned, when working with tab-based browsers like Opera and Firefox, sessions are preserved across tabs....IE deals with sessions just fine.  There is probably some way to adjust the settings in Firefox or Opera, but that is not the default, and will affect most users."

In fact, the way sessions are managed among different browsers is not all that different among MSIE and Opera and Firefox.  You will find that MSIE sessions can be maintained across browser windows, just like in FF or Opera.  However the difference that djohnson noted is because of the way MSIE windows are created vs FF and Opera.

If you choose, in MSIE, "File->New Window", you will find that PHP sessions are preserved from browser window to browser window.

If, OTOH, you start a new window of MSIE using the start menu or desktop icon, a new instance of MSIE, with a new process ID, is started.  *This* instance does not have anything to do with any other previously existing MSIE instances, and, thus, PHP sessions started in this new MSIE process are distinct from PHP sessions in existing MSIE instances.

See the difference?

Whereas FF and Opera apparently always start a new window into an existing FF or Opera process.  Thus PHP sessions are always maintained across browser instances with FF and Opera.
hans at nieser dot net
14-Feb-2006 03:15
FreeBSD users, instead of modifying the PHP5 port Makefile, you can either install the session extension using the www/php5-session port, or you can install several extensions at once (you can pick them from a menu) using the lang/php5-extensions port. Same goes for PHP4
<