在讲这个问题之前让我们来先看一段代码:
dim sql_injdata,SQL_inj,SQL_Get,SQL_Data,Sql_Post
SQL_injdata = "'|and|exec|insert|select|delete|update|count|*|%|chr|mid|master|truncate|char|declare"
SQL_inj = split(SQL_Injdata,"|")
If Request.QueryString<>"" Then
For Each SQL_Get In Request.QueryString
For SQL_Data=0 To Ubound(SQL_inj)
if instr(Request.QueryString(SQL_Get),Sql_Inj(Sql_DATA))>0 Then
Response.Write "<Script Language=javascript>alert('SQL防注入系统提示,请不要在尝试注入!');history.back(-1)</Script>"
Response.end
end if
next
Next
End If
If Request.Form<>"" Then
For Each Sql_Post In Request.Form
For SQL_Data=0 To Ubound(SQL_inj)
if instr(Request.Form(Sql_Post),Sql_Inj(Sql_DATA))>0 Then
Response.Write "<Script Language=javascript>alert('SQL防注入系统提示,请不要在尝试注入!');history.back(-1)</Script>"
Response.end
end if
next
next
end if
这是一段在互联网上广泛流行的ASP防注入的代码,其思想是通过对Post方法和Get方法提交的数据进行检查,通过过滤Insert、Update、And等等这些敏感字符的办法来防止受到SQL注入式的攻击,从理论上来说如果我们过滤了足够多的字符那是绝对可以保证不会受到SQL注入式攻击的,但是请再仔细阅读一下这段代码,注意一下它的判断方式,它是通过instr函数来判断的,也就是说如果我要过滤and字符,实际上被过滤的不仅仅是And这个单词,同时把所有包含and这种字符组合方式的所有单词都给过滤掉了,比如island、mainland、hand…………,如果把这些字符都过滤了还有人会愿意用吗?所以这种过滤敏感字符的方法根本就没有意义,让我比较意外的是这么一个垃圾东西居然在互联网上被人奉为经典的贴来贴去,真是无语。
有人说SQL注入式攻击是因为拼接SQL查询字符串造成的,所以使用存储过程不使用拼接SQL查询字符串的方式可以不受SQL注入式的攻击,真是这样吗?不见得,下面再让我们来看一个存储过程被注入攻击的例子。
存储过程dt_GetNews代码如下:
CREATE PROCEDURE dt_GetNews
@newstype int
AS
select * from news where newstype=@newstype
GO
调用的代码:
<%
dim adoconnection
set adoconnection=server.createobject("adodb.connection")
'…………这里省略了建立数据库连接的相关代码
adoconnection.execute "exec dt_GetNews "+request("newstype")
adoconnection.close
%>
如果request("newstype")的值等于1,运行的结果是返回news表中所有newstype字段为1的记录,但是如果request("newstype")的值是"1;drop table news"呢,返回的结果是news表被删除。
从这个例子中可以看出来即便是用存储过程同样也会被攻击,再说了select * from news where newstype=@newstype难道就不是拼接,所以说拼接SQL查询字符串和SQL注入攻击之间没有必然的联系,存储过程也不一定能防御注入式攻击。
那么究竟怎么写才不会受到SQL注入攻击呢,下面我就介绍一种终极方法,说白了很简单也很原始就是数据类型验证加单引号替换。不管是Oracle、Sql Server还是mySql、Access还是别的关系数据库,字段的类型大体上可以分为两大类:数值型(如:int、float等)和字符型(如:char、varchar等),根据字段类型的不同对应的SQL语句也略有区别,比如:
“Select * from news where newstype=1”里面newstype字段必然是一个数值型的字段,
”select * from news where newstype='社会新闻'”里面newstype字段必然是一个字符型的字段。
针对数值型的字段,我们必须要做的是一定要检查参数的数据类型,比如我们用”select * from news where newstype=”+v_newstype这种方式构造查询语句的时候必须检查v_newstype变量的数据类型,v_newstype至少得是一个数,可以是整数也可以是浮点数,如果作了这样的检查,”select * from news where newstype=”+v_newstype这种方式就绝对不会构造出类似”select * from news where newstype=1;drop table news”这样的语句。ASP相对ASP.Net、JSP等更容易受到攻击的原因,就是因为在ASP中变量可以不用申明以及变量类型不明确导致的。
针对字符型的字段,我们必须要做的是一定要处理单引号('),处理的方法就是将一个单引号替换成两个的单引号(‘'),比如我们用”select * from news where newstype='”+v_newstype+”'”这种方式构造查询语句的时候必须将v_newstype里的单引号替换成两个单引号,因为在SQL中被两个单引号括起来的部分表示一个字符串,而连续的两个单引号则表示一个单引号字符,做了这样的处理以后再来看”select * from news where newstype='”+v_newstype+”'”这种构造方式,当v_newstype的值为:
“社会新闻';drop table news--”
经过一个单引号到两个单引号的替换后v_newstype的值就成了:
“社会新闻'';drop table news--”
构造出来的SQL语句成了:
”select * from news where newstype='社会新闻'';drop table news―‘”
查询的结果是返回news表中newstype字段的值为”社会新闻';drop table news--”的记录,而并不会像之前那样造成news表被删除的后果。
另外,需要做处理的不仅仅是Select语句,包括Insert、Update、Delete、Exec等等都需要处理,大家可以再看看以下这几种注入方式:
在"insert into news(title) values('"+v_title+"')"这种构造中,
当v_title="123';drop table news--'"的时候;
在"update news set title='"+v_title+"' where id="+v_id这种构造中,
当v_title="123'--" 或者 v_id="1;drop table news--" 的时候,所以不光是Select语句的问题,其他语句都可能会有问题,不要仅仅盯着Select
总之,做好了数据类型的验证和单引号字符的处理以后,就算它孙猴子有万般能耐也飞不出我如来的掌心。