防止sql注入和sqlmap介绍

sql注入问题从WEB诞生到现在也一直没停过,各种大小公司都出现过sql注入问题,导致被拖库,然后存在社工库撞库等一系列影响。

 

防止sql注入个人理解最主要的就一点,那就是变量全部参数化,能根本的解决sql注入问题。比如代码里写sql或数据库层面写存储过程,只要不拼接sql语句,不执行动态sql语句,那就能防止sql注入。

但实际情况sql语句可能非常复杂。很多年前有讨论很热的话题,非常复杂的业务逻辑到底用sql语句在存储过程里来实现,还是在代码service层来实现复杂的业务逻辑。比如电信移动很多老项目都是有非常复杂的存储过程或函数来实现业务,这里不说到底放哪里好,因为和实际业务情况有关系。不管把这么复杂的业务逻辑在代码层面service或dao里实现还是存储过程实现,都需要防止sql注入问题

和数据库交互的业务系统,去掉业务逻辑层面,不管应用服务器还是数据库服务器,就剩下sql的操作。这里说下我认为的几种和实际情况最接近的写sql语句的情况:

1、where里是单条件或条件个数明确的sql语句:

比如按照用户ID查询用户信息;1个订单多个产品,按照订单ID,产品ID查询该订单里某个产品的信息,where条件是明确的。

2、where条件不明确的,不明确的原因是需要根据用户的选择作为条件:

比如查询订单,订单有多个元素:OrderID,UserID,ProductID,ProductName,CreateTime,Status,现在是多条件搜索页面,用户可能选择按照CreateTime和Status作为条件查询某个时间段订单状态为支付完成的,where条件为:where CreateTime=.. and Status=..,如果用户按照其他元素搜索,where写法又不一样。

3、where条件是一个in范围的或like查询的:

比如where UserID in (1,2,3,4)。

 

这里最简单的是第1个情况,条件明确,直接把每个确定的条件参数化查询sql即可。

第2个情况稍微复杂点,一般开发人员很容易拼接sql语句,尤其条件非常多的情况下,按照用户查询的不同条件,if else..按照条件直接拼接sql语句,这样就造成了sql注入。实际处理时,可以在第一个if前定义一个List变量,针对每个if条件,如果符合if条件,sql语句就参数化一个占位符,List变量add一个用户传入的条件。下面是示例代码:

if (null != begindate && !"".equals(begindate)) {
// 开始日期
sqlBuilder.append(" and beginDate >= ? ");
params.add(DateUtil.conversionDate6(begindate.trim() + " 00:00"));
}

sqlBuilder用于参数化拼接sql,params是参数化集合List params,后面再参数化sql查询:

Query query = sessionFactory.getCurrentSession().createQuery(sqlBuilder.toString());

private void setQueryParamter(Query query, List params) {
if (!params.isEmpty()) {
for (int i = 0; i < params.size(); i++) {
query.setParameter(i, params.get(i));
}
}
}

第3个情况in会有一个问题,就是比如in(?)?传入的问题,需要把?参数传为一个数组,经常出现的问题可能传入一个字符串导致查询不出来,比如:

String sqlStr = "select * from user i where i.id in (:ids)";

Query query = sessionFactory.getCurrentSession().createQuery(queryString);
query.setParameterList("ids", idsValue);

idsValue可能会被赋值为"'1','2','3'"的字符串,实际应该为String[] splits=...的一个数组。

like里应该如下:

sql.append("and name like ? ");
list.add("%"+param+"%");

而不应该写为:

sql.append("and name like '%?%' ");
list.add(param);

list作为参数化传入的参数。

上面的示例代码是java的示例代码,其他.NET和PHP也可以参考。不同数据库参数化的占位符号不一样,比如msql是@,oracle是:,mysql是?,mysql里用名字取名的占位符是":名字",比如前面的:ids。

 

上面说了如何防止sql注入,下面简单介绍下神器sqlmap,它是一个典型的sql注入检测和利用工具。

sqlmap支持5种sql注入模式:

1、基于布尔的盲注(Boolean-based blind SQL injection),即可以根据返回页面判断条件真假的注入。

比如对参数加一个' and '1'='1和' and '1'='2,如果第一个能查询出来,第二个不行,则说明可以注入,后面可以根据字段and exist(...)不断猜测。


2、基于时间的盲注(Time-based blind SQL injection),即不能根据页面返回内容判断任何信息,用条件语句查看时间延迟语句是否执行(即页面返回时间是否增加)来判断。

比如直接把某个参数的值删除,改为if((ascii(mid(user(),1,1))=103 and sleep(3))如果ascii(mid(user(),1,1))=103条件为真,则会等待3秒才返回结果,如果ascii(mid(user(),1,1))=103为假,则直接返回页面,这样不断猜测user()的第1,2,3个字符。


3、基于报错注入(Error-based SQL injection),即页面会返回错误信息,或者把注入的语句的结果直接返回在页面中。

比如在条件后面增加+and(select 1 from(select count(*),concat((select (select (select concat(0x7e,user(),0x7e))) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a),通过报错把user()显示出来,这个方法是最快的。

这里报错注入的sql很不好理解,首先它实现的前提是如果sql报错了,后台配置的按照sql怎么出错怎么抛出异常到页面(即非友好页面),然后比如这里报错主要依靠floor(rand(0)*2),rand(0)是mysql的一个函数,他会返回0到1之间的随机浮点数,rand(0)*2为1到2之间的随机浮点数,floor是往下取整的函数,比如floor(0.2)=0,floor(1.2)=1。这里group by x就非常重要了,比如我们查询select 1 from information_schema.tables group by rand(0) limit 0,30;将会查询30个1出来,原因是group by 一个随机数,每次都将查询不同的数据出来,limit限制30就显示30条。所以刚才试的sql语句,因为group by x,x的值是随机的,比如x值为~root@127.0.0.1~0,也可能为~root@127.0.0.1~1,group by 这个数据如果遇到两个相同的group key,则就报错了,如果页面吧错误抛出到页面,则可以通过正则匹配~和~符号之间的为user()的值,比如报错:Duplicate entry '~root@127.0.0.1~1' for key 'group_key'。


4、联合查询注入(UNION query SQL injection),可以使用union的情况下的注入。

这个很好理解,就是在select后union把数据查询出来,比如先在查询条件后输入order by 3,order by 4..到order by 8的时候报错了,说明select了7条记录。然后条件后输入 and 1=2 union select 1,user(),database()...查询7个数据出来,由于第一个select后有了1=2条件不成i,因此只会查询我们写的select语句,这样覆盖之前的查询记录返回出我们需要的数据,这个速度也非常快。


5、堆查询注入(Stacked queries SQL injection),可以同时执行多条语句的执行时的注入。

比如用;分隔后执行其他sql语句,一般用于更新数据或执行系统相关命令,比如在参数后面增加; update sysuser set password='' where username='admin',或比如sqlserver执行exec master..xp_cmdshell,执行cmd命令创建远程登录用户等。

 

sqlmap非常强大,可以get,post,httpheader的值变化识别是否存在注入,它可以自动识别MySQL, Oracle, PostgreSQL, Microsoft SQL Server, Microsoft Access, IBM DB2, SQLite, Firebird, Sybase和SAP MaxDB数据库,还有一些常用的绕过防止sql注入的替换符号,也可以自己扩展python脚本绕过WAF之类的防注入程序,常用参数和常用检测命令后续单独再写一个blog文章。

 如需转载,请注明来自:http://lawson.cnblogs.com

posted @ 2015-12-30 20:39  Lawson  阅读(7235)  评论(0编辑  收藏  举报