SQL元数据注射和解决方案

 

 

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调用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标准的保留关键字进行约束

 

 

 

你可能感兴趣的:(解决方案)