php中序列化与反序列化在utf8和gbk编码中测试
php 在utf8和gbk编码下使用serialize和unserialize互相序列化和反序列化会出现无法成功反序列化的问题。
问题出现的原因主要是在不同编码下strlen函数计算中文字符串长度不同的原因。
代码如下 | 复制代码 |
<?php $array=array('title'=>'php教程分享网','url'=>'http://www.111cn.net'); echo serialize($array); //gbk编码 a:2:{s:5:"title";s:13:"php教程分享网";s:3:"url";s:20:"http://www.111cn.net";} //utf8编码 a:2:{s:5:"title";s:18:"php教程分享网";s:3:"url";s:20:"http://www.111cn.net";} ?> |
要解决这个问题就要在反序列化的时候重新修正字符串的长度。
解决方案
代码如下 | 复制代码 |
<?php $str='a:2:{s:5:"title";s:13:"php教程分享网";s:3:"url";s:20:"http://www.111cn.net";}'; $str = preg_replace_callback( $regex , "fixser", $str); function fixser($matches) { return 's:'.strlen($matches[2]).':'.'"'.$matches[2].'"'; } ?> |
可以改成匿名函数
代码如下 | 复制代码 |
<?php $str = preg_replace_callback( ?> |
把复杂的数据类型压缩到一个字符串中
serialize() 把变量和它们的值编码成文本形式
unserialize() 恢复原先变量
eg:
代码如下 | 复制代码 |
$stooges = array('Moe','Larry','Curly'); 结果: |
当把这些序列化的数据放在URL中在页面之间会传递时,需要对这些数据调用urlencode(),以确保在其中的URL元字符进行处理:
代码如下 | 复制代码 |
$shopping = array('Poppy seed bagel' => 2,'Plain Bagel' =>1,'Lox' =>4); echo '<a href="next.php?cart='.urlencode(serialize($shopping)).'">next</a>'; |
margic_quotes_gpc和magic_quotes_runtime配置项的设置会影响传递到unserialize()中的数据。
如果magic_quotes_gpc项是启用的,那么在URL、POST变量以及cookies中传递的数据在反序列化之前必须用stripslashes()进行处理:
代码如下 | 复制代码 |
$new_cart = unserialize(stripslashes($cart)); //如果magic_quotes_gpc开启 |
如果magic_quotes_runtime是启用的,那么在向文件中写入序列化的数据之前必须用addslashes()进行处理,而在读取它们之前则必须用stripslashes()进行处理:
代码如下 | 复制代码 |
$fp = fopen('/tmp/cart','w'); fputs($fp,addslashes(serialize($a))); fclose($fp); //如果magic_quotes_runtime开启 //如果magic_quotes_runtime关闭 |
在启用了magic_quotes_runtime的情况下,从数据库中读取序列化的数据也必须经过stripslashes()的处理,保存到数据库中的序列化数据必须要经过addslashes()的处理,以便能够适当地存储。
代码如下 | 复制代码 |
mysql_query("insert into cart(id,data) values(1,'".addslashes(serialize($cart))."')"); $rs = mysql_query('select data from cart where id=1'); $ob = mysql_fetch_object($rs); //如果magic_quotes_runtime开启 //如果magic_quotes_runtime关闭 |
当对一个对象进行反序列化操作时,PHP会自动地调用其__wakeUp()方法。这样就使得对象能够重新建立起序列化时未能保留的各种状态。例如:数据库连接等。
用例子给你说明一下
代码如下 | 复制代码 |
<?php //声明一个类 class dog { var $name; var $age; var $owner; function dog($in_name="unnamed",$in_age="0",$in_owner="unknown") { $this->name = $in_name; $this->age = $in_age; $this->owner = $in_owner; } function getage() { return ($this->age * 365); } function getowner() { return ($this->owner); } function getname() { return ($this->name); } } //实例化这个类 $ourfirstdog = new dog("Rover",12,"Lisa and Graham"); //用serialize函数将这个实例转化为一个序列化的字符串 $dogdisc = serialize($ourfirstdog); print $dogdisc; //$ourfirstdog 已经序列化为字符串 O:3:"dog":3:{s:4:"name";s:5:"Rover";s:3:"age";i:12;s:5:"owner";s:15:"Lisa and Graham";} /* ----------------------------------------------------------------------------------------- 在这里你可以将字符串 $dogdisc 存储到任何地方如 session,cookie,数据库,php文件 ----------------------------------------------------------------------------------------- */ //我们在此注销这个类 unset($ourfirstdog); ?> b.php <?php
?> <?php //声明一个类 class dog { var $name; var $age; var $owner; function dog($in_name="unnamed",$in_age="0",$in_owner="unknown") { $this->name = $in_name; $this->age = $in_age; $this->owner = $in_owner; } function getage() { return ($this->age * 365); } function getowner() { return ($this->owner); } function getname() { return ($this->name); } } /*还原操作 */ /* ----------------------------------------------------------------------------------------- 在这里将字符串 $dogdisc 从你存储的地方读出来如 session,cookie,数据库,php文件 ----------------------------------------------------------------------------------------- */ $dogdisc='O:3:"dog":3:{s:4:"name";s:5:"Rover";s:3:"age";i:12;s:5:"owner";s:15:"Lisa and Graham";}'; //我们在这里用 unserialize() 还原已经序列化的对象 $pet = unserialize($dogdisc); //此时的 $pet 已经是前面的 $ourfirstdog 对象了 //获得年龄和名字属性 $old = $pet->getage(); $name = $pet->getname(); //这个类此时无需实例化可以继续使用,而且属性和值都是保持在序列化之前的状态 print "Our first dog is called $name and is $old days old<br>"; ?> |
序列化与反序列化语法解析不一致带来的安全隐患
. PHP string serialize() 相关源码分析
------------------------------------
代码如下 | 复制代码 |
static inline void php_var_serialize_string(smart_str *buf, char *str, int len) /* {{{ */
|
通过上面的代码片段可以看到 serialize() 对 string 序列化处理方式如下:
代码如下 | 复制代码 |
$str = 'ryatsyne'; var_dump(serialize($str)); // $str serialized string output // s:8:"ryatsyne"; |
ii. PHP string unserialize() 相关源码分析
---------------------------------------
unserialize() 函数对 string 的反序列化则分为两种,一种是对 `s:` 格式的序列化 string 进行处理:
代码如下 | 复制代码 |
switch (yych) {
|
另一种是对 S: 格式的序列 string 进行处理(此格式在 serialize() 函数序列化处理中并没有定义):
代码如下 | 复制代码 |
static char *unserialize_str(const unsigned char **p, size_t *len, size_t maxlen)
|
从上面的代码片段可以看到 unserialize() 对序列化后的 string 反序列化处理如下:
代码如下 | 复制代码 |
$str1 = 's:8:"ryatsyne";';
|
iii. 语法解析处理不一致导致的安全隐患
-----------------------------
从上述分析过程可以看到 PHP 在反序列化 string 时没有严格按照序列化格式 s:x:"x"; 进行处理,没有对 " 后面的是否存在 ; 进行判断,同时增加了对十六进制形式字符串的处理,这样前后处理的不一致让人很费解,同时由于 PHP 手册中对此没有详细的说明,大部分程序员对此处理过程并不了解,这可能导致其在编码过程中出现疏漏,甚至导致严重的安全问题。
回到文章开头提到的 IPB 漏洞上,利用这个 funny feature of PHP 可以很容易的 bypass safeUnserialize() 函数的过滤:)
代码如下 | 复制代码 |
* mixed safe_unserialize(string $serialized) return false; |
什么是单例模式呢
单例模式顾名思义,就是只有一个实例。
作为对象的创建模式, 单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,
这个类我们称之为单例类。
单例模式的要点有三个:
它们必须拥有一个构造函数,并且必须被标记为private
它们拥有一个保存类的实例的静态成员变量
它们拥有一个访问这个实例的公共的静态方法
和普通类不同的是,单例类不能在其他类中直接实例化。单例类只能被其自身实例化。要获得这样的一种结果, __construct()方法必须被标记为private。如果试图用private构造函数构造一个类,就会得到一个可访问性级别的错误。
要让单例类起作用,就必须使其为其他类提供一个实例,用它调用各种方法。单例类不会创建实例副本,而是会向单例类内部存储的实例返回一个引用。结果是单例类不会重复占用内存和系统资源,从而让应用程序的其它部分更好地使用这些资源。作为这一模式的一部分,必须创建一个空的私有__clone()方法,以防止对象被复制或克隆。
返回实例引用的这个方法通常被命名为getInstance()。这个方法必须是静态的,而且如果它还没有实例化,就必须进行实例化。getInstance() 方法通过使用 instanceof 操作符和self 关键字,可以检测到类是否已经被实例化。
代码如下 | 复制代码 |
header("Content-type:text/html;charset=utf-8"); |
好了,该说书代码了,我们在程序中查询数据的操作会非常非常的多,我们不可能每次都new一个对象,这样太耗费开销了。那么我们怎么办呢,单例模式是个不错的选择。 单例模式:只能实例化一次
下面看一下代码
db.class.php
代码如下 | 复制代码 |
<?php private function __construct(){ public static function getInstance(){ /** /** 数据库的配置文件 db.config.php
$db = array( "host" =>$host, |
该同步登陆需求需满足以下三个关键点:
1)A域名下登陆的用户,跳转到B域名下时实现同步登陆;
2)B域名下点击A域名下的退出链接,实现A域名与B域名的同时退出
3)用户直接访问B域名时,可以自动判断A域名下是否存在用户登录,如果存在,则实现B域名下的用户同步登陆。
首先解决跨域的同步登陆登出,有以下可行的解决思路:
1)两个域共享SESSION服务器,即统一的内存服务器,这样两个域下的SESSIONID会相同,会自动无缝实现同步登陆登出;
但该解决方案需要额外的硬件投入,并且需要懂这方面部署的技术人员
2)通过url传递sessionid
3)通过P3P协议获取跨域的SESSION
为解决该需求,我经过查询各种资料,并拟定了自己的一套解决方案,分享给大家,其要点如下:
1)当用户在A域名下登录后,访问A域名下网站时,会ajax请求B域下的js脚本文件,写入B域COOKIE及SESSION,实现B域下登录;
2)当用户在A域名下退出后,访问A域名下网站时,会ajax请求B域下的js脚本文件(同登录参数不同),清除B域COOKIE及SESSION,实现B域下登出;
3)当用户直接访问B域网页时,请求A域下脚本,判断A域下是否存在登录,如果存在,则将COOKIE及SESSION赋值到当前域的网页中,通过ajax实现当前域
COOKIE及SESSION的写入。
我将a域名设定为A域名;B域名设定为B域名。
以下是相关代码:
代码如下 | 复制代码 |
#############a域名ApiController.php ################# ############# B域名 getbtsck.html ################# |
该方案尚存在的不足:
当用户直接访问B域时,需要加载一次该页面后,才能判断是否在A域登录,并写入当前域(B域)的SESSION
explode() 函数把字符串分割为数组。
语法
explode(separator,string,limit)
例子一
代码如下 | 复制代码 |
<?php |
输出: 0.472347127 1.893372115 2.850965403
例子二
代码如下 | 复制代码 |
<?php |
输出: 893372115 472347127 850965403 PHP
逗号 分割字符串
利用 explode 函数分割字符串到数组
代码如下 | 复制代码 |
<?php $source = "hello1,hello2,hello3,hello4,hello5";//按逗号分离字符串 for($index=0;$index<count($hello);$index++){ ?> |
split函数进行字符分割
代码如下 | 复制代码 |
<?php // 分隔符可以是斜线,点,或横线 |
相关文章
- 这篇文章主要介绍了解决@SpringBootTest 单元测试遇到的坑,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-10-14
Jackson反序列化@JsonFormat 不生效的解决方案
这篇文章主要介绍了Jackson反序列化@JsonFormat 不生效的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-08-10- DVWA (Dam Vulnerable Web Application)DVWA是用PHP+Mysql编写的一套用于常规WEB漏洞教学和检测的WEB脆弱性测试程序。包含了SQL注入、XSS、盲注等常见的一些安全漏洞...2016-11-25
- mail()函数的作用:连接到邮件服务器,利用smtp协议,与该服务器交互并投邮件。注意:1、mail函数不支持esmtp协议,---即,只能直投,不能登陆2、由上条,我们只能直投至最终的收件服务器地址.而该地址,又是在PHP.ini中指定的,所...2015-10-30
- 宿主机使用网线的时候,客户机在Bridged Adapter模式下,使用Atheros AR8131 PCI-E Gigabit Ethernet Controller上网没问题。 宿主机使用无线的时候,客户机在Bridged Adapter模式下,使用可选项里唯一一个WIFI选项,Microsoft Virtual Wifi Miniport Adapter也无法上网,故弃之。...2013-09-19
- System.Text提供了Encoding的抽象类,这个类提供字符串编码的方法。使Unicode字符数组的字符串,转换为指定编码的字节数组,或者反之,看下面的例子...2020-06-25
- 下面小编就为大家带来一篇通过javascript进行UTF-8编码的实现方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2016-07-01
- 这篇文章主要介绍了解决Golang json序列化字符串时多了\的情况,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-12-24
- mail()函数的作用:连接到邮件服务器,利用smtp协议,与该服务器交互并投邮件。注意:1、mail函数不支持esmtp协议,---即,只能直投,不能登陆2、由上条,我们只能直投至最终的收件服务器地址.而该地址,又是在PHP.ini中指定的,所...2015-10-30
- 小编在网上看到最多的就是汉字转换unicode编码了,今天我们看到一个反过来的操作就是把unicode转换成中文了,下面一起来看看 这两天帮别人开发微信平台好友板块,存...2016-11-25
- 下面小编就为大家带来一篇.Net(c#)汉字和Unicode编码互相转换实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
- 序列化是一种对象持久化的手段,普遍应用在网络传输、RMI等场景中,这篇文章主要给大家总结介绍了关于java序列化与反序列化的使用方法,文中通过示例代码介绍的非常详细,需要的朋友可以参考下...2021-07-29
- php测试性能代码 function microtime_float () { list ($usec, $sec) = explode(" ", microtime()); return ((float) $usec + (float) $sec); } functio...2016-11-25
- 下面小编就为大家带来一篇protobuf对象二进制序列化存储(详解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
- 下面我们一起来看看一篇关于phpmyadmin写入一句话木马的测试教程,希望此教程能够对各位有帮助。 方法一,一句话木马偶尔拿到一个config中,发现是root,且还有phpmyadmi...2016-11-25
- 经常看到一些配置文件里面存放的是一些类似带有格式的变量名称和值,其实就是一个序列化的过程,在需要用到这些数据库的时候会进行一个反序列化过程,就是将这个字符串再还原成他原来的数据结构。下面说说php 如何进行数据...2015-10-30
- 这篇文章主要介绍了C#实现的json序列化和反序列化代码实例,本文讲解了两种实现方法,并直接给出代码示例,需要的朋友可以参考下...2020-06-25
- 这篇文章主要介绍了R语言变量重编码、重命名的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-05-06
- 这篇文章主要介绍了C#实现的序列化通用类,实例分析了C#序列化与反序列化操作相关技巧,需要的朋友可以参考下...2020-06-25
- 今天在做公司的项目的时候,遇到一个问题,群聊天记录存入数据库的时候把聊天记录及央视使用16进制转换,我在做将聊天记录导出为text文本的时候,需要将聊天记录先从16进制转...2016-11-25