所谓SQL注入,就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。根据《Oracle编程艺术》上面的引用,简单地说,SQL注入就是通过传入特定参数,然后把这个参数以SQL段形式拼接之后编译执行,重点是SQL “拼接”!
为了说明SQL注入攻击的危险性,《Oracle Database 编程艺术》举了一个例子,下面我们主要是一个更简单的例子去感受一下SQL注入的危险性。
SELECT STUNO, STNAME FROM STUDENT T
WHERE T.CREATE_DATE =
假如我们的程序从学生信息表里读学生学号和姓名信息,通过传入的入学日期(创建日期)参数返回指定学生的信息。
当我们程序使用的是拼接的模式,例如:
DBMS_OUTPUT.PUT_LINE('
SELECT STUNO, STNAME FROM STUDENT T
WHERE T.CREATE_DATE =' || P_CREATE_DATE );
把传入参数直接拼接到SQL语句,然后编译执行。
当 CREATE_DATE 为 VARCHAR 类型,例如存储的格式是'YYYY-MM-DD'这样的字符串格式,有了解过SQL注入的童鞋们一眼就可以看出这个语句灰常容易遭到SQL注入攻击。
在这里,为了说明问题,我们再引入一个表 T_PASSWORD, 存放的是机密的用户密码信息等,当然,我们不想向所有人展示这个表的数据,更甚至的我们不希望那些不必要的人知道有这么一个表。但是,黑客(或者一些恶意的开发人员)可以在上面这个段SQL段利用SQL注入可以读取得到这个表数据。
首先,恶意的开发人员或者用户并不知道有这个表,但是他们可以通过传入的参数 P_CREATE_DATE 等于
'' UNION ALL SELECT '', T.OWNER||'.'||T.TABLE_NAME FROM ALL_TABLES T
这样,那些坏蛋们就可以通过前台我们开发人员提供的这么一个参数端口查看到我们库的库表信息。T_PASSWORD表的存在当然也就暴露咯~
也许,你会说,他们不知道我们的T_PASSWORD有什么字段。是的,他们现在还不知道,但是,他们下一步就会知道了。他们知道了T_PASSWORD表之后,想要了解这个表的字段,一样可以通过传入参数 P_CREATE_DATE 等于
'' UNION ALL SELECT '' , T.COLUMN_NAME ||'.'||T.DATA_TYPE FROM ALL_TAB_COLS T
WHERE T.OWNER = ... AND T.TABLE_NAME = ...
到了这里,你也知道他们可以可以查看 T_PASSWORD 表数据了吧。
上面是假设 CREATE_DATE 为 VARCHAR 类型的字段,这里只是一个假设,并不是一个限制!也许有人会问,不是VARCHAR类型的字段,传入的参数不就是不合法了吗?这样不就可以避免了SQL注入攻击了吗?其实,不然。我们可以拿 CREATE_DATE 为 DATE 类型继续说明一下,即使像 DATE 这样的7字节二进制格式也没有办法避免拼接SQL语句受到SQL注入的攻击。
例如恶意的开发人员或用户只需执行下面代码就可以了:
ALTER SESSION SET
NLS_DATE_FORMAT = ' '' ' ' UNION SELECT TNAME,0,NULL FROM TAB-- '' ';
事实证明, 通过 NLS_DATE_FORMAT 的设置,类似上面的方法,也是可以进行SQL注入的!
(注:上面提到的改变日期格式,本人还没有去了解过。后面有时间去研究研究)
那么,我们如何保护自己呢?最直接的办法就是使用绑定变量。
OPEN C_CURSOR FOR
' SELECT STUNO, STNAME FROM STUDENT T
WHERE T.CREATE_DATE = :P_CREATE_DATE ' USING P_DATE
使用绑定变量,就不会遭遇SQL注入攻击。 其实,绑定变量还能提升程序的运行速度,但是本文旨在提到SQL注入攻击的思想,了解这个黑客常用的手段以及防范方法,其他的不在本文讨论的范围之内,所以就不在此做过多的描述了。