PHP编程
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 程序都应该采取措施来避免这种攻击。为了防止会话固定攻击,可以采取以下措施:
- 不使用 GET 或 POST 变量存储会话 ID(在大多数 PHP 配置下,SID 是通过 Cookie 存储的,因此程序员不需要做任何额外的处理);
- 在每个用户请求时重新生成 SID(在会话开始时使用
session_regenerate_id()
); - 使用会话超时:对于每个用户请求,存储当前的时间戳,在下一个请求时检查超时间隔是否已过;
- 提供登出功能;
- 检查“浏览器指纹”:这是一个哈希值,存储在
$_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。