对字符集、编码和宽字节注入的思考

这篇博文详细介绍宽字节注入以及SQL注入与编码会出现的安全问题:
浅析白盒审计中的字符编码及SQL注入

在此不做搬运工,只做自己的思考和总结。
个人认为学习的正确过程是理解概念、建立概念的关系、推测、实验论证和归纳。

概念:
(1)字符(character)是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。
(2)字符集(Character set)是多个字符的集合,常见的字符集:ASCII字符集、GB2312字符集、BIG5字符集、 GB18030字符集、Unicode字符集等。

GBK编码,是对GB2312编码的扩展,因此完全兼容GB2312-80标准。GBK编码依然采用双字节编码方案,其编码范围:8140-FEFE(高字节从81到FE,低字节从40到FE),剔除xx7F码位

UTF-8 的编码规则很简单,只有二条:
1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。
2)对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。

(3)编码(coding)是信息从一种形式或格式转换为另一种形式的过程,也称为计算机编程语言的代码简称编码。有时指编码的过程,有时指编码的结果。
(4)php的iconv函数作用是字符串按要求的字符编码来转换。

iconv ( string $in_charset , string $out_charset , string $str ) : string

返回的结果是转换字符编码后的字符串。
测试一下:

<?php
	$name = $_GET['name'];
	echo urlencode($_GET['name']);    // %E5%92%8C
	echo '<br>';
	$name = iconv('utf-8', 'gbk', $name);
	echo urlencode($name);           // %BA%CD
?>

浏览器访问并执行脚本:
在这里插入图片描述

结果分析:
浏览器对"和"做Unicode编码,所以是三个字节。当执行iconv函数后,字符编码被转换成GBK,因此"和"的编码是两个字节。

MySQL宽字节注入

<?php
	header('content-type:text/html;charset=gbk');
	
	$conn = mysql_connect('localhost', 'root', 'root') or die('bad');
	mysql_select_db('test', $conn);
	mysql_query('set names gbk', $conn);
	
	$name=addslashes($_GET['name']);
	echo $name.'<br>';
	echo urlencode($name).'<br>';
	
	$sql = "insert into user value('{$name}', 'test')";
	mysql_query($sql, $conn) or die(mysql_error());
?>

执行结果:
在这里插入图片描述
结果分析:
addslashes函数在%27(单引号 ’ )前面插入一个%5c(反斜杠),然后这份数据传递给MySQL。因为MySQL执行了set names gbk,这句代码做了三件事:

set character_set_connection=gbk;
set character_set_results=gbk;
set character_set_client=gbk;

重点在于set character_set_client=gbk,这句代码决定MySQL如何解析对php传输的数据,这里设置了gbk,所以MySQL在处理数据时,将%df%5c看作一个中文字符,%5c仍存在,但反斜杠已经不存在,而单引号逃逸了。

mysql_real_escape_string函数

mysql_real_escape_string函数和mysql_set_query函数配套使用,可以防御宽字节注入,这是方法之一。怎么防御的?

<?php
	header('content-type:text/html;charset=gbk');
	
	$conn = mysql_connect('localhost', 'root', 'root') or die('bad');
	mysql_select_db('test', $conn);
	mysql_query('set names gbk', $conn);
	
	mysql_set_charset('gbk');
	$name=mysql_real_escape_string($_GET['name']);
	echo $name.'<br>';
	echo urlencode($name).'<br>';
	
	$sql = "insert into user value('{$name}', 'test')";
	mysql_query($sql, $conn) or die(mysql_error());
?>

执行结果:
在这里插入图片描述
结果分析:
这里虽然%27前面的反斜杠仍被“吞掉”了,但是MySQL并没有报错,因为这里显示的数据这是浏览器解析的结果。另外,mysql_real_escape_string()的转义作用会考虑到mysql_set_character()设置的字符集,所以这里%df前面也插入了%5c。当MySQL处理这份数据时,两个%5c被去掉(这里的意思是反斜杠已经起到转义作用),而在插入时,%df%27作为gbk的一个字符插入,这不是一个gbk字符,因此变成一个"?"。
在这里插入图片描述
如果传输的数据是%df%df%27,mysql_real_ecsape_string()将%df%df当作一个中文字符,不添加反斜杠转义,就变成%df%df%5c%27。

也就是说,mysql_real_escape_string()考虑mysql_set_charset()的意思是将无法正常解析的字符前面加反斜杠,能够正常解析的字符不加反斜杠。这样一来,从插入数据的结果来看,单引号要么被转义,要么被吞掉,而不会逃逸。(实际上都是反斜杠起到转义作用了)

character_set_client=binary

设置character_set_client=binary也能防御,这是方法之二。

<?php
	header('content-type:text/html;charset=gbk');
	
	$conn = mysql_connect('localhost', 'root', 'root') or die('bad');
	mysql_select_db('test', $conn);
	mysql_query("set character_set_connection=gbk,
				character_set_result=gbk,
				character_set_client=binary", $conn);
	
	$name = $_GET['name'];
	$name = addslashes($_GET['name']);
	echo $name.'<br>';
	echo urlencode($name).'<br>';
	
	$sql = "insert into user value('{$name}', 'test')";
	$result = mysql_query($sql, $conn) or die(mysql_error());
?>

执行结果:
在这里插入图片描述
结果分析:

设置了character_set_client=binary之后,MySQL对php传输的数据就看成二进制形式。

这里的 binary 具有迷惑性,起初我以为二进制数据就不是字符了,%27失去了单引号的闭合作用,所以无论传输什么数据都不会破坏sql语句,但其实不是,这些数据只是被当成一个个单字节字符处理。换言之,现在%27前面的反斜杠一定会起到转义作用,它再也不会被多字节“吞掉”了。这是character_set_client=binary防御宽字节注入的原理。

因此,%df无法插入,变成"?"。
在这里插入图片描述
再插入一个%df:
在这里插入图片描述
两个%df都被转换成"?"。

iconv函数

iconv函数引起的问题是PHP层的问题,如果设置了character_set_client=binary后,多此一举添加了iconv函数就会出问题。

<?php
	header('content-type:text/html;charset=gbk');
	
	$conn = mysql_connect('localhost', 'root', 'root') or die('bad');
	mysql_select_db('test', $conn);
	mysql_query("set character_set_connection=gbk,character_set_result=gbk,character_set_client=binary", $conn);
	
	$name = $_GET['name'];
	$name = addslashes($_GET['name']);
	echo $name.'<br>';
	echo urlencode($name).'<br>';
	
	$name = iconv('gbk','utf-8',$name);          // 字符串由gbk编码转换成utf-8编码
	echo urlencode($name).'<br>'.;
	$sql = "insert into user value('{$name}', 'test')";
	$result = mysql_query($sql, $conn) or die(mysql_error());
?>

执行结果:
在这里插入图片描述
结果分析:
这里的思路是利用 iconv 函数转换编码的时候将反斜杠吞掉。%df%5c是gbk字符,转换成utf-8编码后,已经失去%5c了,%27逃逸。

为什么不是在字符串转换编码的过程,生成一个%5c转义掉%27前面的反斜杠?因为utf-8编码除了第一个字节外,后面的字节都是以 10 开头,而 %5c 是 01011100 ,所以一个中文字符无法在gbk转utf-8后得到一个%5c。

将iconv()的两个“字符集”参数换一下位置:

	$name = iconv('utf-8','gbk',$name);          // 字符串由gbk编码转换成utf-8编码

执行结果:
在这里插入图片描述
结果分析:
这里的思路是利用 iconv 函数转换编码的时候生成一个%5c。将后面紧跟着的%5c转义造,成%27逃逸。

这里为什么不是把%5c吞掉?还是那个原因,utf-8的低位字节范围没有%5c,所以不能够输入两个字节%xx%xx,将紧跟着的%5c吞掉。另外,gbk的低位字节范围中存在%5c,所以能够生成一个%5c。

总结:
总的来说,不同系统的编码不一致就会出现问题。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SQL注入和宽字节注入都是常见的安全漏洞类型。下面我将简要介绍这两种注入方式。 1. SQL注入: SQL注入是一种在应用程序中利用不正确处理用户输入的漏洞,攻击者可以通过在用户输入中插入恶意的SQL代码来执行非授权的数据库操作。这可能导致数据泄露、数据篡改、绕过认证、获取系统权限等问题。 预防SQL注入的措施包括: - 使用参数化查询或预编译语句来处理数据库查询,而不是直接拼接用户输入的数据。 - 对用户输入进行严格的验证和过滤,避免将未经处理的输入直接传递给数据库查询。 - 最小化数据库用户的权限,并且仅分配最低必要的权限。 2. 宽字节注入宽字节注入是一种特定于某些数据库和编码方式的注入攻击方法。它利用了某些编码方式对特殊字符的处理不当,从而绕过了应用程序对输入进行过滤和验证的机制。攻击者可以通过插入宽字节字符来篡改SQL语句或绕过认证。 预防宽字节注入的措施包括: - 使用合适的编码方式,例如UTF-8,以避免特殊字符被误解释。 - 进行输入验证和过滤,确保特殊字符被正确处理,不会导致SQL语句的非预期解析。 - 定期更新数据库和应用程序框架,以修复已知的宽字节注入漏洞。 总的来说,要防止SQL注入和宽字节注入等安全漏洞,应该采取综合性的安全措施,包括输入验证、参数化查询、最小权限原则、使用最新版本的软件以及定期进行安全审计和漏洞扫描。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值