Sessions

Sessions

Sessions 允许 PHP 脚本将数据存储在 Web 服务器上,即使在不同的 PHP 页面之间的请求之间,这些数据仍然可以被使用。每个会话都有一个唯一的标识符,它通过 Cookie 或 $_GET 变量发送到客户端的浏览器。会话在用户关闭浏览器时结束,或者当 Web 服务器删除会话信息时结束,或者当程序员显式销毁会话时结束。在 PHP 中,它通常被称为 PHPSESSID。Sessions 非常有用,可以保护用户不容易读取或写入的数据,尤其是在 PHP 开发者不希望在 Cookie 中传递信息时,因为 Cookies 很容易被读取。Sessions 可以通过 $_SESSION 超全局数组进行控制。存储在这个数组中的数据在会话期间是持久的。它是一个简单的数组。与 Cookies 相比,Sessions 更易于使用,这对 PHP 开发者非常有帮助。通常,Sessions 用于用户登录、购物车和其他需要保持浏览流畅的功能。PHP 脚本可以轻松控制正在发送的 Session Cookie 并管理整个会话数据。Sessions 总是存储在一个唯一的文件名中,文件可以存储在临时文件夹或特定文件夹中(如果脚本指示这么做)。

使用 Sessions

在每个将参与当前会话的 PHP 脚本顶部,必须调用 session_start() 函数。它必须在页面输出的任何内容(例如 echo 或其他)之前调用,否则会导致错误“Headers already sent out”。

session_start();

这个函数会执行以下操作:

  • 检查 $_COOKIE$_GET 中的数据(如果有的话)
  • 如果会话文件不存在于 session.save_path 所指定的位置,则:
    • 生成一个新的唯一标识符,
    • 基于该标识符创建一个新的文件,
    • 并向客户端的浏览器发送一个 Cookie
  • 如果会话文件已存在,PHP 脚本将尝试将文件中的数据存储到 $_SESSION 变量中以供后续使用

现在,你可以用两种不同的方法设置变量,默认方法是:

$_SESSION['example'] = "Test";

或使用已废弃的方法:

$example="Test";
session_register($example);

这两种方法都会将会话变量 $_SESSION['example'] 注册为 "Test"。已废弃的方法不应使用,它仅被列出是因为一些脚本编写者仍在使用旧的方法。推荐使用默认方法。

会话配置选项

PHP 会话易于控制,并且可以通过一些小的配置调整,使会话更安全或更不安全。以下是一些可以通过 php_ini() 函数轻松更改的运行时选项:

配置项 默认值 可变性
session.save_path "/tmp" 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
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
session.use_cookies "1" PHP_INI_ALL
session.use_only_cookies "0" PHP_INI_ALL
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_SYSTEM/PHP_INI_PERDIR
session.bug_compat_42 "1" PHP_INI_ALL
session.bug_compat_warn "1" PHP_INI_ALL
session.hash_function "0" PHP_INI_ALL
session.hash_bits_per_character "4" PHP_INI_ALL
url_rewriter.tags "a=href,area=href,frame=src,input=src,form=fakeentry" PHP_INI_ALL

一个简单的示例代码如下:

// 设置会话保存路径为 "sessions",必须防止读取
session_save_path("sessions"); // 这是对 ini_set("session.save_path", "sessions"); 的替代
// 设置会话 Cookie 的生命周期(虽然可能无效,但请使用)
ini_set("session.cookie_lifetime", time()+60*60*24*500);
// 将会话名从 PHPSESSID 改为 SessionID
session_name("SessionID");
// 启动会话
session_start();
// 设置会话 Cookie(对于某些浏览器,这对于已做的设置可能无效)
setcookie(session_name(), session_id(), time()+3600*24*365, "/");

这个示例代码简单地为下一个年份设置了 Cookie。

结束会话

当用户点击 "Logout" 或 "Sign Off" 时,通常希望销毁所有的登录数据,以防止其他人访问它。会话文件将被简单地删除,同时 Cookie 也会被取消设置:

session_destroy();

使用其他类型的会话数据

像整数、字符串和数组这样的简单数据可以很容易地存储在 $_SESSION 超全局数组中,并在页面之间传递。但在通过赋值存储对象的状态时会遇到问题。可以使用 serialize() 函数将对象的状态存储在会话中。serialize() 会将对象的数据写入一个数组,然后可以将其存储在 $_SESSION 超全局数组中。可以使用 unserialize() 恢复对象的状态,然后在页面中访问该对象。如果对象在多个页面访问期间使用,则在调用 unserialize() 之前必须先定义对象的定义。序列化和反序列化对象时可能会遇到其他问题。

防止会话固定攻击

会话固定(Session fixation)描述的是一种攻击方式,恶意第三方设置(即固定)用户的会话标识符(SID),从而能够访问该用户的会话。在上述会话的基础实现中,这是一个非常实际的漏洞,任何使用会话处理敏感信息的 PHP 程序都应该采取措施来避免这种攻击。为了防止会话固定攻击,可以采取以下措施:

  1. 不使用 GET 或 POST 变量存储会话 ID(在大多数 PHP 配置下,SID 是通过 Cookie 存储的,因此程序员不需要做任何额外的处理);
  2. 在每个用户请求时重新生成 SID(在会话开始时使用 session_regenerate_id());
  3. 使用会话超时:对于每个用户请求,存储当前的时间戳,在下一个请求时检查超时间隔是否已过;
  4. 提供登出功能;
  5. 检查“浏览器指纹”:这是一个哈希值,存储在 $_SESSION 变量中,包含用户代理头、客户端 IP 地址、盐值和/或其他信息。下面的代码示例处理了这些问题,除了检查 referrer(推荐来源)。
$timeout = 3 * 60; // 3分钟
$fingerprint = md5('SECRET-SALT'.$_SERVER['HTTP_USER_AGENT']);
session_start();
if ( (isset($_SESSION['last_active']) && (time() > ($_SESSION['last_active']+$timeout)))
     || (isset($_SESSION['fingerprint']) && $_SESSION['fingerprint']!=$fingerprint)
     || isset($_GET['logout']) ) {
    do_logout();
}
session_regenerate_id(); 
$_SESSION['last_active'] = time();
$_SESSION['fingerprint'] = $fingerprint;

do_logout() 函数销毁会话数据并取消设置会话 Cookie。

Last modified: Friday, 10 January 2025, 12:03 AM