前言
上一篇提到宽字节在SQL注入的相关问题。仔细回想一下整个流程,其实本质是在MySQL中字符编码转换的问题。那么在PHP中进行字符编码转换的相关操作,是不是也会存在这个问题?注意,是PHP中的字符编码转换,在一个程序中难免会使用字符串编码转换,其中iconv
函数就是典型。
iconv函数隐患
先将上一篇的源码改改如下:
示例一:
<?php
$name=$_GET['name'];
$conn = mysqli_connect('localhost', 'root', 'root', 'user');//连接MySQL服务
if (!$conn) {
die('Could not connect to MySQL: ' . mysqli_connect_error());
}
mysqli_set_charset($conn,"GBK");
$name = mysqli_real_escape_string($conn,$_GET['name']);
$name = iconv('GBK', 'UTF-8',$name);
$sql = "SELECT * FROM user WHERE username='{$name}'";
echo $sql."<br>";
$result=mysqli_query($conn,$sql);
@$row = mysqli_fetch_assoc($result);
echo "Your password is:".$row['password']."</br>";
?>
上一篇提到使用mysqli_real_escape_string
和mysqli_set_charset
可以防止宽字节注入的产生。那么在引入了iconv
函数之后,这种防御就变得无效了。
不难看出,在已经设置好编码之后,又进行编码转换,显得有点画蛇添足。
实例二
<?php
$name=$_GET['name'];
$conn = mysqli_connect('localhost', 'root', 'root', 'user');//连接MySQL服务
if (!$conn) {
die('Could not connect to MySQL: ' . mysqli_connect_error());
}
mysqli_query($conn,"SET NAMES 'gbk'");
mysqli_query($conn,"set character_set_connection=gbk, character_set_results=gbk, character_set_client=binary");//防御方式
$name=addslashes($name);
$name = iconv('utf-8', 'gbk',$name);
$sql = "SELECT * FROM user WHERE username='{$name}'";
echo $sql."<br>";
$result=mysqli_query($conn,$sql);
echo mysqli_error($conn);
@$row = mysqli_fetch_assoc($result);
echo "Your password is:".$row['password']."</br>";
?>
第二个实例,是将utf-8
转换成gbk
,由于utf-8
编码是占用3个字节而gbk
编码是占用两个字节。所以多出的这一字节则是我们的突破口。
如上图,当我们输入了
錦
,则依旧产生了注入。这是由于,錦
的utf-8
编码为0xE98CA6
,而GBK
编码则为0xE55c
。而之前提过0x5c
表示的是转义符\
,于是就有了下图的效果,SQL语句中我们加入了一个转义符,从而使单引号逃脱了转义。其他编码的问题
从宽字节的问题,可以体会到当编码处理不当可能会造成隐患。但不仅仅只是字符编码会带来此类问题。不少安全类书籍在介绍SQL注入时,都会提到使用base64
或者url
等一些编码的方式绕过waf
和过滤机制。使用编码的绕过前提程序中要有相应的解码机制。这也在白盒审计中提供了一个思路:当单引号无法使用时,则考虑看看程序的编码问题,或者是否在数据传输时使用某种加密,在进入数据库操作时是否进行相应的解密操作。
防御
首先得理解各个编码存在的差异,这可能有点困难吧,毕竟编码那么多种。其次就是做到前后端编码一致,如果前端展示使用gbk,后端php和mysql也用gbk。编码统一了,自然不会出现乱码,也就不会使用编码转换,避免了隐患。
最后
在写文章的时候遇到蛮多问题,知识点本身记得朦朦胧胧的,梳理完清晰不少,希望能坚持写下去。