PHPSESSION机制以及反序列化

前言

之前接触Session的时候,没有仔细去了解,最近又接触到了php的session反序列化,于是再拿出来梳理一下。

session是干嘛的

设想一个场景,开启一个会话,怎么让会话持续呢?比如一个用户登录一个网站去买一本书,你怎么知道是这个用户要买书而不是另外一个用户呢?而session会存储特定对象的信息,session 其实也是一个身份验证机制。

session咋起作用

在PHP中得调用session_start() 函数来手动开启session机制,当第一次去访问网站时,第一次的响应包里面会携带set-cookie字段,返回一个sessionid,然后服务器会创建与sessionid同名的文件,里面储存了该用户的会话信息。以此来维持会话,且同一个用户访问这个服务器上任何一个页面的时候都会使用同一个sessionid 。如果觉得每一个页面使用session_start比较麻烦,可以在 php.ini配置里将 session.auto_start=1 来开启

session相关配置

1
2
3
session.save_handler        --- session的保存形式,默认为files,以文件形式保存
session.save_path --- session的保存路径
session.serialize_handler --- session序列化存储所用的处理器,默认为php

session反序列化

session序列化存储所用的处理器有三种:

  • php —存储格式:键名+竖线+经过serialize()函数序列化后的值
  • php_binary —存储格式:键名长度对应的ASCII字符+键名+经过serialize()函数序列处理的值
  • php_serialize —存储格式:经过serialize()函数序列化处理的值

存储和取出用的是同一个处理器,不会造成安全问题,而使用不同的处理器就会引发一些安全问题了,下面通过一个典型的demo来解释上面提到的几点

Demo

1
2
3
4
5
6
7
8
9
//demo.php
<?php
ini_set("session.serialize_handler","php");
//ini_set("session.serialize_handler","php_serialize");
//ini_set("session.serialize_handler","php_binary");
session_start();
$_SESSION['My0n9s']=$_GET['a'];
var_dump($_SESSION);
?>

1.png

当我传入?My0n9s=xs时,可以看到本地生成了一个与sessionid同名的sess_xxx文件,我使用的是phpstudy集成环境,默认路径在 D:\phpStudy\PHPTutorial\tmp\tmp

2.png

可以看到php处理器 序列化存储的结果为 My0n9s|s:2:"xs"; 换一下处理器可以看到,php_serialize处理器对应的序列化结果为a:1:{s:6:"My0n9s";s:2:"xs";} ,php_binary处理器对应的序列化结果为:My0n9ss:2:"xs"; 因为ASCII 值6 对应的是ACK这里没显示出来。

我们再来看看由于不同处理器造成的反序列化问题

1
2
3
4
5
6
7
8

//demo1.php
<?php
ini_set("session.serialize_handler","php_serialize");
session_start();
$_SESSION['My0n9s']=$_GET['a'];
var_dump($_SESSION);
?>

demo1.php 我们使用 php_serialize来进行序列化存储

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//demo2.php
<?php
ini_set("session.serialize_handler","php");
session_start();
class Student
{
public $name;
public $age;
function __destruct()
{
echo $this->name."is".$this->age."years old";
}
}
?>

demo2.php 我们使用php处理器

然后我们本地构造一个payload:

1
2
3
4
5
6
7
8
9
10
<?php
class Student
{

public $name='xiaoming';
public $age=8;
}
$a=new Student();
echo '|'.serialize($a);
?>

将生成的传入到demo1.php,查看本地的sess_xxxx文件得到值:

a:1:{s:6:"My0n9s";s:59:"|O:7:"Student":2:{s:4:"name";s:8:"xiaoming";s:3:"age";i:8;}";}

我们再去访问 demo2.php 可以看到:

3.png

成功反序列化。原因在于 php处理器在遇到| 时,会直接把它后面的字符串进行反序列化处理。

造成session反序列化的原因是因为序列化存储时用到的处理器和取出时反序列化的处理器不一致导致的。

采坑

我之前一直在想,序列化和反序列化是什么来触动他们发生的,后来才知道是 session_start 函数来触发的。

官方文档关于session_start的解释:

1
2
session_start() 会创建新会话或者重用现有会话。 如果通过 GET 或者 POST 方式,或者使用 cookie 提交了会话 ID, 则会重用现有会话。
当会话自动开始或者通过 session_start() 手动开始的时候, PHP 内部会调用会话管理器的 open 和 read 回调函数。 会话管理器可能是 PHP 默认的, 也可能是扩展提供的(SQLite 或者 Memcached 扩展), 也可能是通过 session_set_save_handler() 设定的用户自定义会话管理器。 通过 read 回调函数返回的现有会话数据(使用特殊的序列化格式存储), PHP 会自动反序列化数据并且填充 $_SESSION 超级全局变量

重点在最后一句:

PHP 会自动反序列化数据并且填充 $_SESSION 超级全局变量

要开始肝课设了,老拖延症了。