您现在的位置是:首页 >技术教程 >sql注入以及防护sql注入的较为深入一点的探讨网站首页技术教程
sql注入以及防护sql注入的较为深入一点的探讨
前言:对于常见的注入方法我们都已经知道,但是大家有没有想到过,这些东西早就被玩烂了呢?(我朋友也如此提醒我)虽然我讲的东西也被用烂了,但是大家请记住一点,学渗透不仅仅是只学方法,更重要的是学习这个方法的思路,这才是最重要的!
1.攻击方法
1.1宽字节注入
宽字节注入有一个限制,就是需要使用GBK编码,在讲原理之前我们先来看看如下信息
0x5c=
0x27='
0xbf27=¿'
0xbf5c=縗
其原理为一个GBK编码的汉字会占据两个字节(bf是第一个而5c是第二个),当使用GBK编码遇到两个字节都符合其取值范围时,就会将其解析成为一个汉字,第一个字节取值范围为129-254,第二个字节范围为64-254
因此综上所述,当我们遇到反斜杠转义时,就可以在前面输入bf,就可以使其混乱,从而绕过它的转义字符。那如果是使用单引号’时,按照上例,你又该如何进行绕过呢?是的,也是在其前面输入bf。
1.2 TIMING ATTACK
对于这种攻击方式也许有些人会感到陌生,实际上这就是我们所熟知的延时注入方式一种。
mysql数据库中存在一个函数BENCHMARK()函数,该函数是用于测试函数的性能。测试性能是什么意思呢?BENCHMARK(1,ENCODE('1','2')),这个语句是将BENCHMARK(1,ENCODE('1','2'))执行一次,可能一瞬间就完成了,姑且算它0.000001秒就完成了吧,但是大家有没有想过,如果我将这个函数执行10w次,甚至100w次,服务器响应这个请求需要多少时间?呢?BENCHMARK(100w,ENCODE('1','2'))执行时间为4.74s。这种方法替用sleep()被过滤的情况。那如果BENCHMARK()也被过滤了呢?那就需要我们深入探索MYSQL了,此外其他数据库也存在类似于BENCHMARK()的函数,MS SQL Server函数中的WAITFOR DELAY ‘0:0:5'
那我也简单的演示一下代码吧
1 union select if(substring(current,1,1) = char(120),BENCHMARK(5000000,ENCODE('MSG','by 5 seconds'),null) from (select database() as a) as table;
其中是char(120)是小写的x,目的是检测第一个字符是否为x,若是则会发生时间延迟,同理按照基本方法我们还可以得到许多东西。这里就不为大家一一演示了。
1.3 SQL Column Truncation
在mysql的配置选项里,有一个sql-mode选项,当其设置为default时,也就是将STRICT-ALL-TABLESS选项关闭,那我们输入一串超长字符串时,只会提示warning,而不是error(显示error插入不成功,warning成功插入)那如果我们insert一个和管理员名称相同的会怎么样呢?比如admin(100个空格)x,那么由于超过了原来长度限制,会自动删除后面的信息而只保留前面的信息,为什么末尾加一个x呢?要是我输入很长一串,但是服务器检测我输入的一大串都是空白,会不会给我输入那么久的东西都默认为空呢?但是我们输入x后由于x被截断,x也就不会显示在数据库里(我曾经想过如果不截断会不会造成缓冲区溢出的原理,但是想想数据库严格限制了输入的内容,栈溢出也就不存在,应该是这样的吧)。历史上出现过一个真实案例,WordPress有个注册了超长包含admin信息的用户,好在修改密码后被及时发现,没有造成太大的危机。被发现的原因是修改密码后会发送邮件到用户邮箱里。看来做攻击还是得晚上才行。
1.4 XML
XML也是一种常见的文本标记语言,和html一样也存在注入攻击,这里为大家浅浅的讲解一下
<?xml version="1.0" encoding="UTF-8"?>
<USER role="guest_role">
<name>user
</name>
<email>输入的值
</email>
</USER>
但是我们输入特定的语句
user@a.com</email></USER><USER role="admin_role"><name>test</name><email>user@2.com
他们拼接在一起就会变成
<?xml version="1.0" encoding="UTF-8"?>
<USER role="guest_role">
<name>user
</name>
<email>user@a.com
</email>
</USER>
<USER role="admin_role">
<name>test
</name>
<email>user@2.com
</email>
</USER>
咦,变成两组数据了,如果是插入什么东西到数据库的话,那么这个后果不可想象!
1.5 CRLF
CRLF其实是两个字符,其中CR表示 ,LF表示 ,他们都是换行的意思。CRLF通常不同语句之间的分隔符,那么就有可能改变原有语义。
在写入日志文件时,CRLF可能会伪造一条新的记录
比如一个失效的登陆日志为
User login failed for:guest
如果没有处理换行符,那么就可以伪造一个登陆日志
User login failed for:guest
User login successed for:admin
第二条记录是伪造的,不仅仅是log上,只要是使用CRLF的,都有可能存在安全隐患,例如HTTP的头部信息,可以构造出一个XSS攻
击,这个后面章节会讲到。
2.防御手段
1.使用预编译语句
一般情况下,防御SQL注入的最佳方式就是使用预编译语句。如果使用预编译语句,经过拼凑后,原有语句并不会发生过多的改变。就比如用户输入“1;, or '1'='1"字符串,也只会把1;, or '1'='1当作一个整体来查询。
这是PHP预编译绑定变量的示例
$query = "INSERT INTO myCity (Name, CountryCode, District) VALUES (?,?,?)";
$stmt = $mysqli->prepare($query);
$stmt->bind_param("sss",$vall,$va12,$va13);
$va11 = 'Stuttgart';
$va12 = 'DUE';
$va13 = 'Baden-Wuerttemberg';
/* Execute the statement*/
$stmt->execute*();
2.使用存储过程
使用存储过程和预编译类似,存区别在于储过程首先需要将SQL语句定义在数据库中。但是存储过程也存在注入攻击,因此我们需要尽量避免使用动态的SQL语句。要是必须使用无法避免的情况下,那么就要回到严格过滤和编码函数来处理用户的输入的数据了。
下面是一个JAVA调用存储过程的例子:
String custname = request.getParameter("customerName");
try{
CallableStatement cs = connection.prepareCall("{call sp_getAccountBalance(?)}");
cs.setString(1,custname);
ResultSet results cs.executeQuery();
//结果集处理
}catch(SQLException se) {
//日志记录和错误处理
}
custname来接受request.getParameter()(通过容器的实现来取得通过post,get等方式传入的数据)传递用户输入的customerName的值。
这里使用了try catch异常捕获,try中使用CallableStatement是java语句中调用存储过程,RrsultSet是结果集,是存储查询的一个对象,后面的cs.executeQuery()用来执行查询语句。
catch中的SQLException是一个提供了数据库访问错误或者其他错误信息的异常,从而获取异常信息。
3.严格查询数据类型
如下的php代码限制输入的类型只能为integer
<?php
settye($offset,'integer');
$query = "select id,name from products order by name LIMIT 20 OFFSET $offset;";
$query = sprintf("select id,name from produncts order by name LIMIT 20 OFFSET %d;"),
$offset);
?>
此外邮箱的格式,输入的时间都严格按照格式。这能有效的防范sql注入,如果需要用户提交字符串,类似输入电话号码时(字符串类型),规定其字节大小或者其他方法防范。
4.使用安全函数
上面我们讲到的一些例子能绕过编码,因此我们需要一个较为安全的编码函数。我们可以惨考OWASP ESAPI中的实现。
ESAPI.encoder().endodeForSQL( new OracleCodec(),queryparam);
这里博主没有仔细了解太多,只是知道有这么函数。总而言之,从数据库自身来说,我们应该遵守最小权限原则,避免web应用使用高权限,此外还有一点,我们一定要注意数据与代码分离!