【摘要】 SQL Injection 是当前网站安全的主要问题之一。首先阐述了 SQL Injection 网络攻击的基本原理,然后对黑客利用 SQL Injection 技术攻击的一般过程进行了分析。在此基础上针对 SQL Injection 攻击的具体预防措施进行了详细的分析和研究,并且提出了现有防范技术所存在的一些问题及其解决方法。碧森尤信
1 引 言
SQL Injection攻击具有很大的危害,攻击者可以利用它读取、修改或者删除数据库内的数据,获取数据库中的用户名和密码等敏感信息,甚至可以获得数据库管理员的权限。如果能够再利用SQLServer扩展存储过程和自定义扩展存储过程来执行一些系统命令,攻击者还可以获得该系统的控制权。而且,SQL Injection 也很难防范。网站管理员无法通过安装系统补丁或者进行简单的安全配置进行自我保护,一般的防火墙也无法拦截 SQL Injection 攻击。
在目前国内的动态网站中,使用 ASP 和 Access 或 SQLServer的占 70%以上, PHP和MySQL占 20%,其它的不足 10%。
本文将主要分析在 ASP 和 SQL Server 环境下的 SQL Injection攻击及其防范方法,但多数内容同样适合于其它的动态网站环境,希望能对安全工作者和网站开发程序员构建安全的动态网站有一定的借鉴作用。
2 SQL Injection 原理
结构化查询语言(SQL)是一种用来和数据库交互的文本语言。SQL Injection 就是利用某些数据库的外部接口把用户数据插入到实际的数据库操作语言(SQL)当中,从而达到入侵数据库乃至操作系统的目的。它的产生主要是由于程序对用户输入的数据没有进行严格的过滤,导致非法数据库查询语句的执行。
下面将以一个 ASP 登陆页面为例来说明 SQL Injection 的攻击,该页面接收用户输入的用户名和口令,并以此生成一个SQL 查询语句访问数据库。如果该查询成功,将返回一条用户记录,用户登录成功。其中生成查询串的语句如下:
var sql =“select * from users where username=‘”+ username+“‘and password=’”+ password +“‘”;
如果攻击者在登录页面中指定了下面这样的输入数据:
Username: 'or 1=1--
Password:aaa
那么,查询字符串就变为:
select * from users where username=''or 1=1-- and password='aaa'
显然上面查询语句将返回表中所有用户记录,攻击者将以表中第 1 个用户的身份登录。
从上面的例子我们可以知道,SQL Injection就是利用用户提交的数据,把我们想要的SQL语句插入到系统实际的SQL语句当中,从而收集程序及服务器的信息,获取想要得到的资料。
3 SQL Injection 常用方法与技巧
下面简要介绍一下攻击者在运用 SQL Injection 时常用的方法和技巧。
3.1 判断程序运行环境及注入点
传统的注入判断方法是把“’“号添加到提交请求的末尾,然后根据服务器返回的提示信息来判断数据库的类型和是否存在注入漏洞信息。但是部分对 SQL 注入有了解的程序员,会把单引号过滤,在这种情况下使用单引号测试,是测不到注入点的。
经典的注入判断方法是 1=1、1=2 测试法。就是把“ and 1=1”和“and 1=2”插入到提交请求的 URL 的末尾,如果前者正常显示,后者出错,则存在注入漏洞;如果都报错,则不存在漏洞。例如:
A、http://www.xxx.com/show.asp?id=49 and 1=1
B、http://www.xxx.com/show.asp?id=49 and 1=2
如 A 正常显示 B 报错,为可以注入,否则不可以注入。当然,实际应用的时候可能还需要和“’”、“)”等符号进行一些灵活的组合。
3.2 猜测表名和列名
(1)参数类型
在猜测表名和列名之前,首先要确定查询语句中的参数类型。参数类型主要有下面 3 种:①数值参数,形如 Select*from 表名 where 字段=ID;②字符串参数,形如 Select*from 表名 where 字段=’关键字’;③模糊参数,形如 Select*from 表名 where 字段 like’%关键字%’。
(2)注入形式
对于这 3 种不同的参数类型,在注入时应选择不同的注入形式。对于数值参数,使用“IDand 查询条件”进行注入,如:Select*from 表名 where 字段=IDand 查询条件;对于字符串参数,使用“关键字’and 查询条件”进行注入,如:Select*from 表名where字段=’关键字’and查询条件;对于数值参数,使用“关键字%’and 查询条件”进行注入,如:Select*from表名 where 字段 like’%关键字%’and 查询条件。
根据参数的不同,需要选择相应的注入形式进行攻击,比如对数值型参数,我们可以使用下面的语句猜解表名:
select * from 表名 where ID=49 And(Select Count(*)from Admin)>=0
如果返回的页面与原页面相同,说明附加条件成立,即表示 Admin 存在,反之,即不存在。如此循环,直至猜到表名为止。表名猜解出来后,将 Count(*)替换成 Count(字段名),再用同样的原理猜解字段名。
3.3 获得字段值
在得到数据库的表名和列后,可以使用 ASCII 逐字解码法得到字段值。
例如,已知表 Admin 中存在 username 字段,首先,我们取第 1 条记录,测试长度:http://www.xxx.com/show.aspid=49and(select top1len (username)from Admin)>0 如果top1的username长度大于0,则条件成立;接着>1、>2…… >n 继续测试,一直到条件不成立为止。此时的 n 值即第1 条记录的 username 的长度。比如当>7 成立,>8不成立,就是len(username)=8。
然后,用 and(select top1 asc(mid(username, 1,1))from users)>n 来测试 users 表中第 1 个用户的 username 的第 1 个字符。注意:一般是 48-122,48 是数字 0,122 是 z。然后 asc(mid(username,n,1))继续下去,就可以得到用户名。其它字段的解码同理。
3.4 进一步攻击
除了可以对数据库的数据进行上述操作外,SQL 注入还可以结合系统存储过程,来执行一些系统命令进行SQL Injection 攻击,如:利用 xp_cmdshell 扩展存储以 SQL Server 用户的身份在数据库服务器上执行命令;利用xp_regread扩展存储读取注册表的键值,也包括SAM(只要SQLServer是以一个本地账号运行的);用其它的扩展存储改变服务器设置;在联合服务器上执行查询;创建客户扩展存储从而在 SQL Server 进程内运行 exploit;用'bulk insert'语句去读服务器上任何文件;用bcp 在服务器上创建任何文本文件。
4 SQL Injection 的防范
从上述讨论的 SQL Injection 的原理和攻击方法可以看出SQL Injection 攻击的一般特性。由此可以总结出针对 SQL Injection 防范的一般方法,这里主要介绍输入验证和数据库安全策略。
4.1 输入验证
由于 SQL Injection 就是利用某些数据库的外部接口,把攻击者想要执行的 SQL 语句插入到系统实际的 SQL 语句当中,从而达到入侵数据库乃至操作系统的目的。它的产生主要是由于程序对用户输入的数据没有进行细致的过滤,导致非法数据库查询语句的执行,所以首先要对用户提交或者可能由用户提交的数据进行输入验证。
输入验证是一个很复杂的问题。输入验证的途径可以分为以下几种:整理数据使之变得有效;拒绝已知的非法输入;只接受已知的合法的输入。
解决方案 1 有很多概念上的问题。首先,开发者没有必要知道也未必完全了解非法数据由什么组成,因为新形式的非法数据随时都可能产生。第 2,改变数据会改变它的长度,这样会导致前面提到的问题,也就是可能产生新的非法数据。最后,还有需要对系统已有数据的重用的二次注入的问题。
解决方案 2 也会遇到和 1 的一些相似的问题,因为新的攻击技术也在不断发展,所依据的现有非法数据会过时。
解决方案3可能是3种方法中最好的,但是比较难于实现。
从安全角度来考虑,可能最好的解决方法是把解决方案2 和 3 结合起来——只允许合法地输入,然后再寻找过滤非法字符。但是这样也会面临的一个问题,就是会存在一些可能被非法数据利用的合法字符和符号。例如:带有连字符的名字的问题,Question Bassington-Bassington我们必须在合法输入里允许连字符号,但字符串'--'在 SQL-Server 里却有着其它的意义。如果数据整理结合了非法字符验证时就可能产生另外的问题导致 SQL Injection。假设我们应用“非法字符探测器”来探测‘--’、‘select’和‘union’后使用“数据整理过滤器”删除单引号,攻击者就可以指定这样的输入:uni'on sel'ect@@version-'-,经过过滤和整理后,因为单引号被过滤器删除了,最后提交的数据变为:union select@@version--,则可以导致非法SQL 查询语句执行,以致信息泄漏。也就是说攻击者可以把单引号散布于它的已知的非法字符串里来躲避检查。
所以如果想要获得最好的安全状态,目前最好的解决办法就是对用户提交或者可能改变的数据进行简单分类,分别应用正则表达式来对用户提供的输入数据进行严格的检测和验证。
第 1 类:只是简单的字符和数字组合。这也是应用最广泛的提交数据,进行验证也很简单,应用如下的正则表达式就可以进行验证:
[^\w+$]
第 2 类:需要包含一些符号在内的组合,例如 EMAIL、密码等。对这些复杂的情况我们要分别对待。已知输入格式的,就构造该格式的正则表达式进行严格过滤;未知输入格式的,首先匹配其中的分隔符:[空格]、[/*]、[*/]、[[] 、[]] 等,然后再对其中的攻击特征字进行匹配。
总结集合目前已知的 SQL Injection 攻击方法,可以得出目前的攻击中所用到的符号集合作为非法符号集合,通过正则表达式予以过滤并返回错误。
已知字符和合法符号:a-z、A-Z 、@、- _.
已知非法符号:“’”、“;”、“=”、“(”、“)”、“/*”、“*/”、“%”、“+”、“”、“>”、“<”、“--”、“[”、“]”;
其实只需要过滤非法的符号组合就可以阻止已知形式的攻击,并且如果发现更新的攻击符号组合,也可以将这些符号组合增添进来,继续防范新的攻击。特别是空格符号和与其产生相同作用的分隔关键字的符号,例如“/**/”,如果能成功过滤这种符号那么很多注入攻击将不能发生,并且同时也要过滤它们的十六进制表示 “%XX”。
则可以构造如下正则表达式:
(|\'|(\%27)|\;|(\%3b)|\=|(\%3d)|\(|(\%28)|\)|(\%29)|(\/*)|(\%2f%2a)|(\*/)|(\%2a%2f)|\+| (\%2b)|\<|(\%3c)|\>|(\%3e)|\(--))|\[|\%5b|\]|\%5d)
根据上述的正则表达式,最后可以提供两个函数,代替ASP中的Request函数,可以防范大多数的SQL注入,函数如下:
Function SafeRequest(ParaName,ParaType)
'---传入参数---
'ParaName:参数名称-字符型
'ParaType: 参数类型-数字型(1 表示以上参数是数字或字符,0 表示以上参数为其它)
Dim ParaValue
Dim re As RegExp
Set re = New RegExp
If ParaType = 1then
re.Pattern =“[^\w+$]”
Else
re.Pattern =“(|\'|(\%27)|\;|(\%3b)|\=|(\%3d)|\(|(\%28)|\)|(\%29)|(\/*)|(\%2f%2a)|(\ */)|(\%2a%2f)|\+|(\%2b)|\<|(\%3c)|\>|(\%3e)|\(--))|\[|\%5b|\]|\%5d)”
End If
ParaValue=Request(Para)
If re.test(ParaValue)? then
Response.write "参数不符合要求,请重新输入!"
Response.end
SafeRequest=ParaValue
End function
并且,如果有新的 SQL Injection 方法出现,只需要把新方法的特征字符添加进 Pattern 中就可以继续防止新的 SQL Injection 了。但是对一些特定的复杂的输入就要用专门的正则表达式进行过滤。
虽然我们可以通过输入验证来组织绝大多数的 SQL Injection攻击,但是难保WEBc程序员在开发中还是会忽略一些验证,导致 SQL Injection 攻击,所以 SQL Injection 防范最重要的一点是必须防范SQLServer,完全依靠外部的防范并不能保证服务器绝对安全。
4.2 安全策略检测
但有些时候,由于Web程序员的疏忽,或者攻击者躲避了检测,导致 SQL Injection 攻击。所以要降低 SQL Injection 对SQL Server 的威胁,还是要对 SQL Server 进行基本的安全策略检测。
以下是一些构建 SQLServer 服务器时需要的注意事项简明清单。
(1)决定连接到服务器的方法:使用“Network utility”检验你使用的网络库是可用的。
(2)检查存在的账号:为程序创建低权限账号;删除不需要的账号;确保所有的账号都有一个健壮的密码。
(3)检查哪些对象存在:许多扩展存储可以安全地删除,并考虑删除一些包含扩展存储的dll;删除所有的数据库实例——比如'northwind'和'pubs'数据库。
(4)检查哪些账号可以访问对象:应用程序用户所使用的访问数据库的账号应该只拥有对所需要的对象的最小访问权限。
(5)检查服务器的补丁状况:安装一些针对 SQL Server 的缓冲区溢出与格式字符串攻击和一些其它的安全补丁,并及时去查看是否还有更新的补丁。
(6) 检验日志记录,明确日志记录的意义:通过以上的检测,基本上可以了解我们的 SQLServer 的安全状况,并且可以把逃脱输入验证的 SQL Injection 造成的威胁降到最低。
5 结论
随着网络的普及和飞速发展,网络安全问题也越来越引起人们的重视。本文从网站架设的主要脚本语言和数据库系统的安全入手,分析了 SQL Injection 的存在、危害和一些攻击方法,最后给出了防范 SQL Injection 攻击的具体方法。希望能对网站管理人员和开发人员提供一些帮助。
参考文献:
[1] Joel Scanbray,Shema Mike.Web application security secrets and Solutions[M].北京:清华大学出版社, 2003.
[2] Kevin Spett. White paper SQL injection [EB/OL]. 2002. http://www.spidynamics.com/whitepapers/WhitepaperSQL Injection.pdf .
[3] Anley Chris.Advanced SQL injection in SQL server applications[EB/OL]. 2003.http://www. nextgenss. com/papers/advanced_sql_injection.pdf.
[4] Zou Cliff C,Don Towsley,Gong Weibo.E-mail virus propagation modeling and analysis [EB/OL]. 2003.http://tennis.ecs.umass. edu/~czou/research/emailvirus-techreport.pdf.
[5] 潘志强,岑进锋.黑客攻防编程解析[M].北京:机械工业出版社, 2003.
[6] Maximum Security:A hacker's guide to protecting your internet site and network[M].Macmillan Computer Publishing, 1998.
[7] McClure Scambray S,杨洪涛.Windows 2000 黑客大曝光[M].北京:清华大学出版社, 2002.
[8] Adida Ben. Securing the Web [M]. Massachusetts Institute of Technology, IEEE Internet Computing, 1997.
[9] Richards W Tevens, Addison Wesley, 范建华, 等. TCP/IP 详解[M].北京:机械工业出版社, 2000.
[10] 蒋东兴,林鄂华.Windows Sockets网络程序设计大全[M].北京:清华大学出版社, 1999.
[11] Stanek William R.Windows 2000 脚本编程[M].北京:中国水利水电出版社, 2001.
[12] Holzner S,钟鸣,王君,等.XHTML技术内幕[M].北京:机械工业出版社, 2001.