Author : kj021320


台上 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 :,具体可以参看这里 获取更多的技术资料


kj021320自以为是的举例子说明


kj021320 : 例如在ORACLE SELECT /*+ PARALLEL(kj,4) */ COUNT(*) FROM kj 这样子可以让ORACLE调用4CPU来处理这个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​​标准的保留关键字进行约束​​