MYSQL 字符编码利用
这算是一种绕过手段了, 原理就是通过 mysql 默认字符集和客户端程序连接 mysql 的字符集不一致, 导致处理上出现差异
先创建一个表:
CREATE TABLE `test` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(255) COLLATE latin1_general_ci NOT NULL,
`password` varchar(255) COLLATE latin1_general_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;
这个表中指定的字符集是 latin1
随便插入一条数据:
INSERT `test` VALUES (1, 'admin', 'admin');
下面是 test.php 代码:
<?php
$mysqli = new mysqli("localhost", "root", "root", "test");
/* check connection */
if ($mysqli->connect_errno) {
printf("Connect failed: %s\n", $mysqli->connect_error);
exit();
}
$mysqli->query("set names utf8");
$username = addslashes($_GET['username']);
if ($username === 'admin') {
die('Permission denied!');
}
/* Select queries return a resultset */
$sql = "SELECT * FROM `table1` WHERE username='{$username}'";
if ($result = $mysqli->query( $sql )) {
printf("Select returned %d rows.\n", $result->num_rows);
while ($row = $result->fetch_array(MYSQLI_ASSOC))
{
var_dump($row);
}
/* free result set */
$result->close();
} else {
var_dump($mysqli->error);
}
$mysqli->close();
这时候访问这个 http://hostname/test.php?username=admin%c2
, 会发现正常查询除了 username = admin
的结果, 这个 %c2
被忽略掉了
在安装 MYSQL 之后, 没有做任何操作的情况下, 在 mysql 的 控制台输入 SHOW VARIABLES LIKE 'character_set_%';
字符集应该是下面的样子
在 set names utf8;
再看就会变成:
这就是问题所在了, 刚刚我们创建的表是 latin1 字符集, 而 php 连接的时候使用 utf8, 会涉及到字符集的转换
mysql 会将收到的请求数据从 character_set_client
转换为 character_set_connection
, 再从 character_set_connection
转换为内部操作字符集
于是就会有以下转换过程:
utf8 -> utf8 -> latin1
而如果都是 utf8 自然是没有问题的, 可是这里不一致了, 就需要考虑到 latin1 和 utf8 之间的差异, 以及处理时对不兼容的字符如何处理了
我们以此访问:
http://hostname/test.php?username=admin%E6
http://hostname/test.php?username=admin%E6%88
http://hostname/test.php?username=admin%E6%88%91
发现前两个都返回了 admin 的信息, 说明忽略了, 而第三个没有
%E6%88%91
是汉字 我
的 url 编码, latin1 并不支持汉字, 所以第三个抛错了:
'Illegal mix of collations (latin1_general_ci,IMPLICIT) and (utf8_general_ci,COERCIBLE) for operation '=''
前两个编码不完整被忽略所以返回了结果
你可能会想到的是尝试 %00 或者 %F0, 但是你必须要知道的是 utf8 编码有自己的规范, 有一些字节是不允许出现的, 只能使用 U+0000 到 U+10FFFF :
MYSQL 字符集的问题
%F0 满足 utf8 的范围, 但是却无法获取信息, 这是因为另一个 mysql 的特性
在 mysql 中, utf8 只支持 3 个字节, 而 utf8m64 才是完整的 4 字节编码