Author : kj021320
Team : I.S.T.O
台上 kj021320穿着一套便服,一点都不像安全技术人员...
台下一群 黑客 骇客 红客 白客 砍客 蓝客 灰客 漂客...
还有一堆开发工程师和架构师 貌似很期待的在听课,接收kj021320的培训
kj021320 : hi,大家好...今天我要跟大家一起讨论的是SQL注射...
台下一片郁闷声响起
有位骇客郁闷道 : 哎~咋又是注射!现在都快绝种的了啊!大企业都用JAVA DOTNET参数绑定,其他的公司都过滤了单引号,数字的就进行类型转换,哪来注射啊?大哥!...
kj021320 : .... ....
kj021320 : 只要使用了变量绑定就不存在注射了吗?就不用关注这方面的了?
开发工程师 : 废话!你到底懂不懂预编译的啊?
kj021320 : .... ....
kj021320 : 大家如果有兴趣了解预编译的细节,可以去google yahoo 找找 <<窥探SQL预编译内幕>>,这是偶写的烂文大家多多提点。
听到这里,台下开始认真重视起来了!kj 稳定一下在座的情绪继续说
kj021320 : 现在的企业WEB应用随着需求越来越复杂,新的安全问题也在诞生,那么我们的安全解决方案跟上这些脚步没有呢?我接下来分析SQL注射的问题
kj021320一边在 黑板上画一边说
现在来回眸一下 过去比较土的SQL注射攻击
下面一段代码
<%
id=request("id")
title=request("title")
sql="select * from news where id=" & id &" and title='" & title & "'"
...
%>
这样的代码一看不及格了就不多说
后来又有人把这样的SQL注射攻击分类为 字符串 和 数值 , 然后总结出过滤转换规则
<%
id=cint(request("id"))
title=replace(request("title"),"'","''")
sql="select * from news where id=" & id &" and title='" & title & "'"
...
%>
再过了段时间,又有新的提倡使用参数绑定,乃至今天
以下为 JAVA 代码
PreparedStatement ps=con.prepareStatement("select * from news where id=? and title=?");
ps.setInt(1, id);
ps.setString(2, title);
ResultSet rlst=ps.executeQuery();
后来大家都很放心的认为,采用了预编译SQL 就不会再有注射出现...
其实隐患才刚刚开始...
现在WEB应用越来越复杂,讲求的是网站重构,代码重用,可能3句话的东西,现在整合为1句话!
例如以下的方式
<%
id=cint(request("id"))
title=replace(request("title"),"'","''")
sql="select * from news where id=" & id &" and title='" & title & "' order by posttime"
...
sql="select * from news where id=" & id &" and title='" & title & "' order by updatetime"
...
sql="select * from news where id=" & id &" and title='" & title & "' order by viewcount"
%>
整合为
<%
id=cint(request("id"))
title=replace(request("title"),"'","''")
orderCol=replace(request("order"),"'","''")
sql="select * from news where id=" & id &" and title='" & title & "' order by "&orderCol
%>
很容易就能看到以上是存在攻击的,不多说!而在预编译SQL中可以使用类似过滤吗?
"select * from news where id=? and title=? order by ?"
kj021320 : 哈呵嘻噶!
台下有开发工程师摇摇头...
kj021320 : 那么你们是怎么写代码的啊?
开发工程师 : ps=con.prepareStatement("select * from news where id=? and title=? order by "+order);
kj021320 : 所以问题就出现了...(SQL注射,我们又见面了),那么该如何修补呢?
开发工程师 : ..... .....
架构师 : 是不是可以将order by 的字段放到一个 list里面 ,然后每次传进来的时候,判断变量是否包含在list里面,这样就OK了啊!
kj021320 : 很好非常好,这是一个很简单的解决方案!但是如果表结构将来会变化,或者现在需要按3个字段来排序,但是以后因为业务需求,要求加入更多的排序字段呢?.....
kj021320 : 那你就负责维护那个白名单的LIST吗?或者说,不局限于这个order by 有可能在 group by 或者 count max length 这些里面需要动态更换
架构师 : ..... .....
台下有位漂客老听着这些解决方案有点郁闷,大声问道
漂客 : 这样的SQL注射可以带来什么样的攻击啊?或者可以怎么利用
kj021320 : 把字段直接换为 攻击的函数,或者一段SQL语句
kj021320 : 例如 getNews.asp?order=2;drop table admin--
骇客 : 那我知道怎么利用这样的攻击了!(阴阴的笑~) 不过现在好象没有注射工具支持这样的猜解以及攻击,包括NBSI HDSI CASI PANGOLIN ...
kj021320 : 呵,自己动手丰衣足食,手动无敌...
开发工程师&架构师 在一边郁闷,不明白kj021320跟骇客们在说什么东西
kj021320 : 言归正传...那到底用什么样的方式更好的解决呢!在这里考考 架构师和开发工程师一个问题!
kj021320 : 如果我需要建立一个表,表名字为 KJ 021320 中间有空格,那么在MSSQL里面怎么做呢?
开发工程师 : ~~~~恩~!好象是用 [] ,用个中括号罩着,create table [KJ 021320] 这样!
kj021320 : 对! 但是我不建议使用这样的方式,更提倡使用 " 双引号,例如 create table "KJ 021320" ,都能理解吧?
大伙都齐口回答 明白
kj021320 : 那么,现在攻击者会尝试绕过,例如 select * from news order by "id" ,攻击者有可能输入双引号怎么办呢?
开发工程师 : ................ 没想过!
架构师 : 两个转换为一个 select * from news order by """id"
kj021320 : 很好很强大... 那么现在我们来总结一下 关于这类型注射的过滤规则和防止方案
kj021320又在 黑板上画
1. 针对接收回来的参数进行替换
例如:
<%
order = """" & replace(request("order"),"""","""""") & """"
sql="select * from news order by " & order
%>
2. 针对预编译的修改
( 这里介绍JDBC 其他的ADO.NET可以参考着做 )
我们可以对 PreparedStatement Connection CallableStatement 重新封装,我提供思路
Connection con = DriverManager.getConnection(url);
SecurityConnection seccon=new SecurityConnection(con);//封装的安全连接类
SecurityStatement sstmt=seccon.prepareStatement("select * from news where id=? and title=? order by ?");//封装过的安全statement类
sstmt.setColumn(3,order);//新加入的方法 动态设置字段 其内部实现也是 采用替换
刚刚看了MSSQL是可以用双引号",MYSQL 是采用反引号`来转义的 而ORACLE是用 双引号" , 其他的数据库可以参看对应的官方文档,所以需要对证下药,因为现在的数据库驱动包没提供解决方法需要自己实现...
kj021320 : 这个问题 完满的结束...
开发工程师 : 现在的SQL注射就只需要解决这个问题吗?
kj021320 : NO,现在的应用开始BT起来,很多东西不可取的都应用起来了...
kj021320 : 下面需要介绍另外的语法上面的SQL注射以及解决方案
架构师 : 哦? 还有?
kj021320又在 黑板上画
因为有业务需求,需要动态使用排序的方式例如:
<%
orderByType = request("orderType")
sql="select * from news order by time "& orderByType
%>
以上即使 替换了 " 也会有问题,而且不能够加入 类似 " " 这样的标识符过滤方式
类似这样的应用例如 在 模糊查询跟 直接查询之间的切换
<%
selectType = request("seltype")
sql="select * from news where title " & selectType & " ? "
%>
这个查询类型可以为 like 可以为 = <> 等等
类似这样的传递有点BT, 为了满足开发人员的需求以及欲望
这里只能把SQL92标准 SQL3标准 以及当前数据库应有的 保留关键字 放入一个清单里面
然后逐一判断
ASP
<%
sqlkeywords = "select,union,asc,desc,in,like,into,等等"
selectType = request("seltype")
if not inkeywords(selectType) then response.end ' inkeywords自己实现循环判断的函数
sql="select * from news where title " & selectType & " ? "
%>
JDBC
Connection con = DriverManager.getConnection(url);
SecurityConnection seccon=new SecurityConnection(con);//封装的安全连接类
SecurityStatement sstmt=seccon.prepareStatement("select * from news where id=? and title=? order by ? ?");//封装过的安全statement类
sstmt.setColumn(3,order);
sstmt.setKeyWord(4,orderType);
OK问题解决...
kj021320 : OK,很好很完美!
开发工程师在钻牛角尖
开发工程师 : 我记得ORACLE里面可以使用注释来说明SQL语句运行的方式的,那么...
kj021320 :对,具体可以参看这里 http://blog.csdn.net/I_S_T_O/archive/2008/02/15/2096228.aspx 获取更多的技术资料
kj021320自以为是的举例子说明
kj021320 : 例如在ORACLE中 SELECT /*+ PARALLEL(kj,4) */ COUNT(*) FROM kj 这样子可以让ORACLE调用4个CPU来处理这个SQL语句,那又有什么问题呢?
开发工程师 : 我想让里面的注释里面可以动态更换
开发工程师 : 例如 insert /*+ append*/ into kj select * from kj021320 不用日志形式,或者 insert /*+ PARALLEL(kj,2)/ into kj select * from kj021320 并发方式
kj021320 : ..... ..... (kj很纳闷,有点冲动把这个开发工程师拖出去打...)
kj021320 : 对于这样的方式不允许使用,因为DBA不一定同意你随便操作SQL里的性能更换,特别是数据库操作写不写日志,采用多少个CPU来运算
kj021320 : 这个应该对于开发人员是透明的,如果需要接触到这个层面,应该由DBA介入
OK现在来回顾一下今天所讲到的问题:
1. 数据库内对象的动态更变,例如一些字段名,表名,视图等,需要采用对应数据库的标识符来转义以及限制
2. 对于结构体内的动态更换,采用SQL标准的保留关键字进行约束