• 0x01 引入
  • 0x02 关于PHP的编码问题
    • 1 PHP内部编码
    • 2 关于PHP的乱码问题
  • 0x03 MYSQL的UTF-8编码与字符差异
    • 1 MYSQL的UTF-8编码
    • 2 MYSQL的字符差异问题
    • 3 补充
  • 0x04 参考资料

0x01 引入

最近稍稍研究了下关于PHP的内部编码的问题,以及mysql的字符差异的问题,分享下心得,如果有误请大家及时指正。

至于为什么要介绍mysql字符差异问题,是因为普遍将其原因归纳于PHP编码与mysql的UTF-8编码不统一,但实际上这个只是mysql单方面的原因而与PHP的编码方式无关。

另外本文不涉及编码的具体方式。

先引入如下:


<?php$m="可";
echo strlen($m);  ?>

问这里输入几?2还是3呢?

不多废话直接开始测试下就知道了,截图如下:

PS:enca是linux下一款用于探测和修改文件编码方式的软件

可以看到,在文件的编码方式为UTF-8的时候,代码输出为3,而文件编码为GB2312的时候,代码输入为2。

也就是说,这里这个该文件的编码类型决定了文件中那个 “” 字的长度。那么原因是什么呢?

PS:平时linux和windows下文本文件默认都是以UTF-8的形式保存

0x02 关于PHP的编码问题

2.1 PHP内部编码

这里需要说明一下,首先PHP内部的字符串只是一个字节序列,并不会保留任何的编码信息,所以可以说说PHP是不关心编码的,即PHP里一个字符就是一字节。

所以你的字符串啊,各种来源的输入啊之类的PHP都是可以识别的,至于能不能显示,能不能处理又是另一回事了。因为无论你在文本编辑器中保存了什么、或是从数据库中得到它,它已经被编码了,就是说已经被决定了占用了几个字节了,在传递给PHP的时候就已经是以字节的形式传递过去了。

所以说对于PHP来说,一个字符及对应一个字节,换言之,我们可以通过控制数据指针访问到字符串的每一个字节。

而PHP对编码的唯一要求就是它要能够保存为ascii的形式,因为它需要从中获取指令。但是这一点我们并不需要担心,因为大部分编码方式都向下兼容ascii。但是也有例外,比如UTF-16就不兼容ascii,所以我们不能用UTF-16来保存PHP源代码。

PS:UTF即 Unicode Translation Format ,即把Unicode转作某种格式的意思。标准的UNICODE编码又称UTF-16。所以UTF-16也就可以理解为通常意义上说的UNicode编码。非要说二者区别的话,也就是UTF-16的本质算是一种存储方式吧。

如下图做个测试,我们以UNICODE保存PHP源码,然后访问如下图:

![这里写图片描述](https://img-blog.csdn.net/20161123185239899)

发现PHP都没有被解析直接返回了源码。即PHP源文件已经无法被正常解析了。也就是说我们可以将PHP源代码保存为任何ASCII兼容的编码,因为如果编码的前128个代码点与ASCII相同,那么就意味着PHP可以解析它。或是说PHP支持该编码方式。

PS:一个语言支持某个编码方式是什么意思?例如,Javascript支持Unicode,事实上,Javascript中的任何字符串都是Unicode编码的,即不能在Javascript中有一个不是Unicode编码的字符串。其他语言只是编码感知。在内部,它们以特定的编码,通常是Unicode存储字符串。反过来,他们需要被告知或尝试检测与文本有关的一切的编码。他们需要知道保存源代码的编码,他们应该读取的文件的编码,要输出文本的编码,并且它们根据需要转换编码,其中一些表现形式是作为中间人的Unicode,很显然的代表就是python。

那么到这儿我们就可以理解引入部分那个例子了。

因为对于PHP来说,每一个字符就是一个字节,所以strlen和内部编码无关,因此它将计算字节数,而不是字符数。所以该文件的编码类型决定了文件中那个 “” 字所占用的字节数,从而决定了strlen计算的数目。

所以这些“可读的字符”的东西是我们人自己的事情,但是PHP语言其实并不在乎。

2.2 关于PHP的乱码问题

有了上面的知识,那么我们可以发现其实PHP内部的编码方式是什么并不是问题关键,问题的关键在于内外内外编码方式的差异。因为PHP不试图解释,转换,编码或以其他方式干扰获取到的字节序列。 该文件甚至可以包含二进制数据或是PHP内部编码并不支持的UNICODE编码的文件,PHP不在乎这些。

但内部和外部编码必须匹配,譬如我们的汉字要想输入在html页面上,需要设置meta或是用header,将输出的编码方式设置位支持汉字的编码方式。

所以乱码问题总结就是一个,内部编码与外部编码差异

所以最简单的解决方案是啥?如果是正常情况下使用时:

  • 将PHP的内部编码设置为UTF-8。

  • 将所有源文件保存为UTF-8。

  • 使用UTF-8作为输出编码(不要忘记发送合适的内容类型头)。

  • 将数据库连接设置为使用UTF-8(在MySQL中为SET NAMES UTF8)。

  • 如果可能的话配置一切编码为UTF-8。

PS:这里为啥是UTF-8而不是别的?因为就我查阅的资料来看,它可以表示所有Unicode字符,因此可以取代所有现有的7位和8位编码,并且因为它与ASCII是二进制兼容的,即每个有效的ASCII字符串也是一个有效的UTF-8字符串。

这里再提及一下关于PHP中设置编码的函数,对单个字符串编码的函数就不赘述了,这里只说常用的配置整体环境编码的iconv_set_encoding,过去常用这个来配置默认编码方式,


bool iconv_set_encoding ( string $type , string $charset )type 的值可以是以下其中任意一个:input_encodingoutput_encodinginternal_encoding

对应与PHP.ini中的iconv.input_encodingiconv.output_encodingiconv.internal_encoding

但是由于上述三个在PHP5.6之后就已经废弃,三者被统一被default_charset代替,所以没必要再介绍了,而且现在大部分PHP环境的都是5.6以上了。

所以就这里说说default_charset,即默认编码方式,简单来说就是在Content-type:xxxx中输出的默认的字符编码,设置了这个,Content-type就会是设置的值。默认情况下是ISO8859-1,通常叫做Latin-1。但通常我们都会修改为UTF-8

0x03 MYSQL的UTF-8编码与字符差异

3.1 MYSQL的UTF-8编码

为什么这里要单挑出UTF-8来讲,因为在MYSQL中除了UTF-8编码,其他编码都和普通一样没有赘述的必要。

MySQL的UTF-8实际上是完整的UTF-8字符集的大部分实现,而非完整实现。具体来说,MySQL的UTF-8数据编码最多使用3个字节,而编码完整的UTF-8字符集需要4个字节。所以如果需要支持例如星形符号等需要四字节编码的字符,MySQL的UTF-8就无力了。但是从MySQL 5.5.3起,增加了对utf8mb4字符集的支持,每个字符使用最多4个字节,从而支持完整的UTF-8字符集。因此,如果使用MySQL 5.5.3或更高版本,一般设置编码为utf8mb4而不是UTF-8

以上便是关于MYSQL的UTF-8编码的简介。

3.2 MYSQL的字符差异问题

实际上这个在HITCON 2016babytrick题目最后用到了这个,但是可能相对来说更关注__wakeup失效的漏洞了,关于最后的绕过的解释很多WP都把原因归为这里PHP不是UTF-8,而对应的MYSQL执行了mysql_query("SET names utf8")操作,所以产生的MYSQL字符差异。

但是就通过上述关于PHP内部编码的分析,我们可以知道,其实这里和PHP内部编码没有关系的,而且题目并没有明确的地方有说明PHP的编码方式。但是问题关键还是在这个问题上,无论PHP是什么编码,根据之前的分析,都不会影响到MYSQL。这样光说也说不清出,直接看下面的例子:


<?php$con = mysql_connect('localhost','root','');mysql_query("set names utf8");mysql_select_db("ctf");if(stripos($_GET['name'],'bendawang')!==false){$name = 'GET OUT!';}else{$name=$_GET['name'];}$sql = "select * from admin where username='$name'";$result = mysql_query($sql);$num = mysql_num_rows($result);if($num>0){echo '<h1>Success!</h1>';print_r(mysql_fetch_array($result));}else{echo "GET OUT!";}?>

这是admin的表结构

我们通过某种方式提前知道了admin表里面有一条记录的username字段值为Bendawang,但是我们要想办法绕过stripos的检测。

先试一试如下:

果然不行,那么我们试试这个。

发现成功绕过并且得到结果了。

那么这是什么原因呢?真的是因为PHP和MYSQL的编码方式的差异吗?

此时,我们可以查看下此时执行的mysql日志

能够看出实际上PHP还是把Àutf-8 byte%C3 %80)传过去了,所以说实际上这里和PHP的编码并没有关系,那么也就是说这个就只是单纯的mysql的内部的问题。

我们再换一种验证方式。

现在,我们再往表里插上这样两条数据如下:


insert into admin values(unhex('62656E646177616E67'),'1'),(unhex('62656E64C38077616E67'),'2');

其中unhex('62656E646177616E67')=bendawangunhex('62656E64C38077616E67')=bendÀwang,插入完成后如下图:

现在我么来执行这么几条指令来观察下:

不知道大家看出蹊跷来没有,在查询的时候mysql默认bendawangbendÀwang是等效的,但是如果真的相比较却又是不同的。

就好比PHP的弱类型比较一样,也可以理解为MYSQL一种牺牲安全性的人性化设计,考虑到不同国家的编码方式不一样。

就好比你在浏览器里面输入http://www。baidu。com仍然可以访问到http://www.baidu.com一样。

所以这里我总结了一下可以这样利用的MYSQL字符。如下(重复就没有整理了):

列1 列2
À a
Ç c
È e
Ì i
Ñ n
Ò o
Š s
Ù u
ý y
Ž z

3.3 补充

最后还要说明下我们在刚开始插入数据的时候执行的语句是


insert into admin values(unhex('62656E646177616E67'),'1'),(unhex('62656E64C38077616E67'),'2');

为什么不执行下面这个语句呢?


insert into admin values('bendawang','1'),('bendÀwang','2')

因为我的演示环境的windows下的mysql的shell环境,对于不识别的字符统统换成问号?(0x3F),也就是说我们所输入的À就不再是%C3 %80了。如下:

但是如果换成linux下的mysql的shell环境就可以用第二种插入方式,如下:

这也是编码方式的问题。linux的shell默认是utf-8编码,windows的shell默认编码是windows-1252

0x04 参考资料

http://kunststube.net/encoding/

http://www.i18nqa.com/debug/utf8-debug.html

http://stackoverflow.com/questions/7861358/strange-characters-in-database-text-%C3%83-%C3%83-%C2%A2-%C3%A2-%E2%82%AC

https://vigilance.fr/vulnerability/MySQL-SQL-injection-via-multi-byte-characters-5885

关于php内部编码与mysql字符差异问题的研究相关推荐

  1. 修改mysql字符编码出现Job failed to start解决办法

    修改mysql字符编码出现Job failed to start解决办法 在python mange.py shell下填充MySQL数据库的时候,发现汉字不能输入. 于是要修改一下MySQL数据库编 ...

  2. 修改数据库mysql字符编码为UTF8

    修改数据库mysql字符编码为UTF8 Mysql数据库是一个开源的数据库,应用非常广泛.以下是修改mysql数据库的字符编码的操作过程.步骤1:查看当前的字符编码方法 mysql> show ...

  3. php 删除mysql 返回_php 返回mysql字符编码与删除字符编码

    php 返回mysql字符编码与删除字符编码 function Ebak_GetSetChar($char){ global $empire; if(empty($char)) { return '' ...

  4. mysql utf-8不支持生僻字_关于 MySQL UTF8 编码下生僻字符插入失败/假死问题的分析...

    1.问题:mysql 遇到某些中文插入异常 最近有同学反馈了这样一个问题: 上述语句在脚本中 load 入库的时候会 hang 住,web 前端.命令行操作则要么抛出 Incorrect string ...

  5. mysql字符为utf8_设置mysql字符编码为utf8

    在windows下开发的JavaWeb项目,运行测试没有任何问题,后来部署到阿里云租来的linux服务器上遇到了中文乱码的问题,排查后发现是mysql字符编码设置的问题. 进入mysql,用" ...

  6. mysql 字符编码查询

    mysql 字符编码查询 1.查询所有表的编码 SELECT TABLE_NAME,TABLE_COLLATION FROM information_schema.TABLES 2.查询数据库的编码 ...

  7. mysql字符编码的设置以及mysql中文乱码的解决方法

    最近在开发过程中,使用到mysql的数据库,而在将中文数据插入到数据库的时候出现了数据乱码的问题,在网上找了很多方法,问了很多人,试了很久才发现网上有的方法是不行的,因此在此记录下,以便他人查找. 查 ...

  8. MySQL字符编码设置

    通过show variables like 'character_set%%';查看编码 修改mysql的编码方式可以有以下几个: 1.通过配置文件修改my.ini(windows下)或/etc/my ...

  9. 使用charCodeAt()和charAt()方法,根据Unicode 编码,转换字符

    1.charCodeAt() 方法 charCodeAt() 方法可返回指定位置的字符的 Unicode 编码.这个返回值是 0 - 65535 之间的整数. 方法 charCodeAt() 与 ch ...

最新文章

  1. bzoj 3864: Hero meet devil [dp套dp]
  2. 如何编程实现一个基本的微分器
  3. 缓冲流、转换流、序列化流
  4. c# 深拷贝各种实现方式
  5. java中容易混淆的方法_java中容易混淆的区别
  6. Java的接口与继承
  7. 华为前端工程师分享:查明网站访问故障原因,教你4招快速应对
  8. python制作一个简单的udp聊天器
  9. Android 渗透测试学习手册 第七章 不太知名的 Android 漏洞
  10. Android native进程间通信实例-binder篇之——解决实际问题inputreader内建类清楚缓存...
  11. SpringCloud工作笔记086---SpringBoot启动报错:No active profile set, falling back to default profiles
  12. Struts2 中继承ActionSupport类
  13. SAP-PM设备模块-PM主数据之设备主数据
  14. 用了TCP协议,就一定不会丢包嘛?
  15. intel 服务器最新cpu,英特尔发布至强 Xeon W-3300 系列服务器处理器:最高 38 核
  16. [知乎]关于WindowsXPx64SP2系统的说明
  17. [前端] marquee使用
  18. mapbox,使用maptalks绘制3D建筑图形
  19. 和GGA的第一次亲密接触
  20. 《顶级摄影器材》系列丛书首发式上海隆重举办

热门文章

  1. 任务一深度思考之测试
  2. 数据仓库工具箱 第10节 金融服务
  3. 免费网盘如何选择@2020年
  4. ppt里插入python(code)代码高亮
  5. .net.3.5sp1_隐藏的宝石-与旧的3.5 SP1帖子不同
  6. Linux里面的oa环境是什么,Linux下oa环境搭建
  7. windows10+python3.7.0(anaconda)+MeCab安装总结
  8. 亚太成为第一季度全球存储市场唯一增长地区;亚马逊云科技在中国区域推出Amazon ECS Anywhere | 全球TMT...
  9. vmware走主机代理
  10. [coreseek/sphinx学习笔记1]--简介