php session反序列化

关于Session

Session,在汉语中表示通话、会话、对话(期)、话路[对谈时间]的意思,其本来的含义一个终端用户与交互系统进行通信的时间(间隔),通常是指从注册(进入系统)到注销(退出系统)之间所经过的时间。比如打电话时从拿起电话拨号到挂断电话这中间的一系列过程可以称之为一个Session。有时候我们可以看到这样的话“在一个浏览器会话期间,…”,这里的会话一词用的就是这个意思,是指从一个浏览器窗口打开到关闭这个期间。Session在我们的网络应用中就是一种客户端与服务器端保持状态的解决方案,有时候Session也用来指这种解决方案的存储结构,

Session对象,就是客户端浏览器与服务器之间建立的互动信息状态。每一个不同的用户连接将得到不同的Session,也就是说Session与用户之间是一种一对一的关系。Session在用户进入网站时由服务器自动产生,并在用户正常离开站点时释放。使用Session的好处就在于,可以将很多与用户相关的信息,例如用户的帐号、昵称等保存到Session中;利用Session,可以跟踪用户在网站上的活动。例如:当你上网进入一个网站时,如果你没有登陆,无论你访问哪几个页面都会跳转回登陆页。还有就是你在购物时,不可能把你的东西放到别人的购物车里去,这就得用一个信息变量来判断!

如果能够提供一些按需生成的动态信息会使web变得更加有用,就像给有线电视加上点播功能一样。这种需求一方面迫使HTML逐步添加了表单、脚本、DOM等客户端行为,另一方面在服务器端则出现了CGI规范以响应客户端的动态请求,作为传输载体的HTTP协议也添加了文件上载、cookie这些特性。其中cookie的作用就是为了解决HTTP协议无状态的缺陷所作出的努力。至于后来出现的Session机制则是又一种在客户端与服务器之间保持状态的解决方案。

session的本质和cookie是差不多的,保存着http状态信息。简单来说、Session是一次浏览器和服务器的交互的会话,会话是啥呢?就是我问候你好吗?你回答说很好。就是一次会话,那么对话完成后,这次会话相当于就结束了,但为什么会出现Session会话呢?因为我们用浏览器访问网站用的是http协议,http协议是一种无状态的协议,就是说它不会储存任何东西,每一次的请求都是没有关联的,无状态的协议好处就是快速;但它也有不方便的地方,比如说我们在login.php登录了,我们肯定希望在index.php中也是登录的状态,否则我们登录还有什么意义呢?但前面说到了http协议是无状态的协议,那访问两个页面就是发起两个http请求,他们俩之间是无关联的,所以无法单纯的在index.php中读取到它在login.php中已经登陆了的;为了解决这个问题,cookie就诞生了,cookie是把少量数据存在客户端,它在一个域名下是全局的,相当于php可以在这个域名下的任何页面读取cookie信息,那只要我们访问的两个页面在同一个域名下,那就可以通过cookie获取到登录信息了;但这里就存在安全问题了,因为cookie是存在于客户端的,那用户就是可见的,并且可以随意修改的;那如何又要安全,又可以全局读取信息呢?这时候Session就出现了,其实它的本质和cookie是一样的,只不过它是存在于服务器端的。

 

具体来说cookie机制采用的是在客户端保持状态的方案,而Session机制采用的是在服务器端保持状态的方案。同时我们也看到,由于在服务器端保持状态的方案在客户端也需要保存一个标识,所以Session机制可能需要借助于cookie机制来达到保存标识的目的,但实际上还有其他选择。例如,我们经常用到的会员卡,也就相当于这种情况。消费到了一定程度就有奖,就如下面例子说明:

 

1.发给顾客一张卡片,上面记录着消费的数量,一般还有个有效期限。每次消费时,如果顾客出示这张卡片,则此次消费就会与以前或以后的消费相联系起来。这种做法就是在客户端保持状态。

 

2、发给顾客一张会员卡,除了卡号之外什么信息也不纪录,每次消费时,如果顾客出示该卡片,则店员在店里的纪录本上找到这个卡号对应的纪录添加一些消费信息。这种做法就是在服务器端保持状态。

 

Session的产生和保存

首先,当我们需要使用Session时,我们要首先打开Session,开启Session的语句是session_start();必须将这个函数置于最先,而且在它之前不能有任何输出,否则会报错。它的作用是打开Session,并且随机生成一个32位的session_id,session的全部机制也是基于这个session_id,服务器就是通过这个唯一的session_id来区分出这是哪个用户访问的:

 

<?php
highlight_file(__FILE__);
session_start();
echo "session_id 为: ".session_id()."<br>";
echo "COOKIE 为: ".$_COOKIE["PHPSESSID"];

 

 

 

并且如果我们进行修改PHPSESSID的值的时候,会发现session_id的值也会被进行修改

 放包后的结果:

 接下来就来看看session它是怎么保存的,因为我使用的phpstudy进行搭建的环境,所以我们可以通过php.ini找到我们保存session的目录

在这里找到我们对应的php版本,然后去点击查找session存放的位置

 

 我的在这里,然后我们找到对应的目录,看到session的存放形式是以sess_+session_id进行命名的。由于我们之前修改了cookie中的PHPSESSID的值,可以看到存放的session文件的命名也发生了改变,从532a52f76c8f62734d720578e27b9bfd变为了123,由此我们可以推断,我们可以通过修改cookie中的PHPSESSID的值来修改session存放的文件名。

 如果我们对于session进行赋值的话,那么里面的内容会发生怎样的变化呢?

<?php
highlight_file(__FILE__);
session_start();
$_SESSION['kode'] = "people";
$_SESSION['0xkode'] = "a people";
echo "session_id 为: ".session_id()."<br>";
echo "COOKIE 为: ".$_COOKIE["PHPSESSID"];

 

看到其存储格式为 键名|序列化数据。

既然这里面提及了序列化,那么我们就应该回想起今天的主题——反序列化

但是他是怎么与反序列化联系的呢?请接着往下看

 

Session反序列化原理

首先我们再来看看session_start()函数,前面我们看到的是没有打开Session的情况下它是打开Session并且返回一个session_id,但假如我们前面就已经打开了Session呢?这里我们再来看看官方文档:

 这里看到会自动进行反序列化数据,所以说,这里并不需要什么unserialize函数便可以自动进行反序列化操作。

因为我们传入的是键值对,那么session序列化存储所用的处理器肯定也是将这个键值对写了进去,那我们怎么让它正好反序列化到我们传入的内容呢?这里就需要介绍出两种处理器的差别了,php处理器写入时的格式为键名+竖线|+经过serialize()序列化处理后的值那它读取时,肯定就会以竖线|作为一个分隔符,前面的为键名,后面的为键值,然后将键值进行反序列化操作;而php_serialize处理器是直接进行序列化,然后返回序列化后的数组,那我们能不能在我们传入的序列化内容前加一个分隔符|,从而正好序列化我们传入的内容呢

设置为php_serialize:

 当为php时:

 这里我原本是想在php.ini进行设置session.serialize_hander的值,但是不知道怎么修改后sess_文件里面的值还是没有变化,所以就直接在页面进行了设置。

来道例题:

exp.php

 <?php
highlight_file(__FILE__);
error_reporting(0);
ini_set('session.serialize_handler','php');
session_start();
class exp{
    public $exp1;
    function __destruct()
    {
        eval($this->exp1);
    }
} 
?>

session.php

 <?php
highlight_file(__FILE__);
ini_set('session.serialize_handler','php_serialize');
error_reporting(0);
session_start();
$_SESSION["0xkode"] = $_GET['kode'];  ?>

我们进行构造我们的反序列化的链子:

 因为前面提到将session.serialize_handler设置为php的话,其实是有一个|的,键名+|+序列化后的值,我们可以看到我们将exp.php的session.serialize_handler的值时设置为php的,那么他获取的sess_532a52f76c8f62734d720578e27b9bfd文件内的值应该是:键名+|+序列化后的值,而如果是由session.php生成的值是这样的

 那么我们携带这个session值去访问exp.php的话,由于:|后面的O:3:"exp":1:{s:4:"exp1";s:10:"phpinfo();";}";} 将会被exp.php页面理解为这是序列化后的结果,由于前文提到了session_start()函数会自动进行反序列化操作

所以我们exp.php页面会自动将|后面的O:3:"exp":1:{s:4:"exp1";s:10:"phpinfo();";}";} 进行一个反序列化操作,最终到达命令执行的效果

如图:

 

 

serialize_handler的值决定了php储存session数据的方式,共有三种:

serializer实现方法
php 键名 + 竖线 + 经过 serialize() 函数反序列处理的值
php_binary 键名的长度对应的 ASCII 字符 + 键名 + 经过 serialize() 函数反序列处理的值
php_serialize(php>5.5.4) 把整个$_SESSION数组作为一个数组序列化

由此可以知道,造成session反序列化的条件就是:同一服务中session处理器设置(session.serialize_handler)出现了不统一。

参考链接:https://www.freebuf.com/articles/web/264740.html

http://arsenetang.com/2021/08/31/%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E7%AF%87%E4%B9%8Bsession%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/#3-%E6%9C%89%E5%85%B3%E7%9A%84%E9%85%8D%E7%BD%AE

 

posted @ 2024-01-26 20:10  kode  阅读(45)  评论(1编辑  收藏  举报