SQL注入攻击和网页挂马
2009-05-14 13:34 csma 阅读(1238) 评论(1) 编辑 收藏 举报1. 概述
网 页挂马这个话题想来大家并不陌生。为什么有这么多的网页上存在着木马去攻击普通用户?不可否认,相当一部分网页原本就是恶意的:网页的作者故意在上面放上 木马,然后通过各种手段引诱用户去浏览。但是绝大多数被挂马的网页原本是正常的网页,例如普通的教育网站,购物网站等等,只是网页被攻击者恶意修改后插入 了木马代码。
那么,攻击者是如何能够恶意修改一个正常网页的呢? 换句话说,一个网站是如何被“黑”的?一个最常见的攻击方法是SQL注入(SQL Injection)攻击。事实上,就在今年的五月份,爆发了一次大规模的网页攻击活动【1】。据估计,约有12万个网页被恶意修改插入木马代码,而攻击者采用的手段就是SQL注入攻击。
那么,什么是SQL注入攻击?攻击者是如何利用SQL注入攻击篡改网页的?Web开发人员又应该如何防范SQL攻击?这就是我们这篇文章要阐述的问题。
2. SQL注入攻击
我们从一个简单的例子【2】开始。下面这段代码用来构造SQL查询命令。
var strUserAccount;
strUserAccount = Request.form ("UserAccount");
var sqlQueryString = "select * from Orders where UserAccount = \'" + strUserAccount + "\'";
//执行SQL Query …
这段代码很简单:给出UserAccount,查询其对应的订单信息。
例如,如果输入UserAccount值为100,那么构造的查询命令就是:
SELECT * FROM Orders WHERE UserAccount = \'100\'
从功能上说这段代码非常正确,完全没有任何问题。但是开发人员往往忽视安全方面的考虑:如果用户(攻击者)提供的输入数据是恶意的, 程序的表现行为是什么?
· 如果是一个不那么友好的攻击者,可能输入UserAccount值为100\' or 1=1 --,构造的查询命令就是:
SELECT * FROM Orders WHERE UserAccount = \'100\' or 1=1 --
执行这个查询命令,就会返回所有用户的订单,导致商业机密信息的泄漏。
· 如果是一个非常不友好的攻击者,可能输入UserAccount值为100’; drop table orders --,构造的查询命令就是
SELECT * FROM Orders WHERE UserAccount = \'100\'; drop table orders --
“;”用来分割SQL语句。“--”表明当前行剩余的内容是注释。这段SQL语句的语法是完全正确的,那么一旦执行,就会先执行正常的查询工作,然后删除订单数据库。后果非常严重吧。但这还不是最糟糕的的情况。
· 如果是一个非常非常不友好的攻击者,可能输入UserAccount值为100’; exec xp_cmdshell(‘fdisk.exe’) --,构造的查询命令就是
SELECT * * FROM Orders WHERE UserAccount = \'100\';
exec xp_cmdshell(‘fdisk.exe’) --
在权限允许的情况下,这段SQL语句的执行后果,就非常非常严重了。
通过上面的这个例子可以看出,SQL注入攻击的定义是:
· 被攻击系统采用字符串接收用户输入,并构造相应的SQL语句来执行数据库操作。
· 开发人员没有对用户输入字符串的合法性做足够验证。
· 攻击者在该字符串中注入的恶意代码,在数据库系统(如SQL Server)上执行。
3. SQL注入攻击和网页挂马
当WEB应用需要后台数据库支持的时候,系统就存在着SQL注入攻击的风险。
那么,攻击者到底是如何利用SQL注入攻击来达到网站挂马的目的?我们就拿开始提到的今年五月份的攻击举例:
· 攻击者通过自动扫描工具,发现存在SQL注入攻击安全漏洞的网站。
· 攻击者实施SQL注入攻击,将恶意的脚本代码插入到SQL数据库的相应数据中。插入的恶意脚本如:<script src=http://恶意网站/攻击.js>
· 恶意的脚本文件(js)试图利用一系列常用软件的安全漏洞,如微软IE的安全漏洞,Adobe Flash的安全漏洞,Real Player的安全漏洞,还有许多流行的国内软件的安全漏洞,如迅雷下载,暴风影音等等。
这样,一个原本正常的网站就被挂上了恶意代码。当用户浏览该网站的时候,网页的动态构造中往往需要从后台SQL数据库取出相应数据并插入到构造的网页中。在这个过程中,恶意的脚本代码也作为数据的一部分被插入到网页中。
如果普通用户的机器没有及时安装厂商提供的安全补丁,或者攻击者利用的安全漏洞是0-day安全漏洞,没有任何安全补丁的话,用户的机器就会被木马感染。
4. Web开发人员如何防范SQL注入攻击
下面是开发过程中,防范SQL注入攻击的三个建议。
· 验证用户输入
· 使用Stored Procedures和相应的参数传递。
· 数据库访问帐号的最小化权限(Least-Privilege)
4.1验证用户输入
既然SQL注入攻击的原理是通过在用户输入的字符串中插入恶意代码,那么验证来自用户的输入就是防范攻击的自然方案。不过,在设计验证代码时需要注意下面两点。
4.1.1在服务器端验证输入,而不仅仅是在客户端验证。
一个常见的设计误区是检验代码仅仅放在客户端上。Web开发中往往通过运作在客户网页的JavaScript或VBScript检查用户的输入值。如果校验错误的话就直接通知用户错误信息。客户端校验通过后才会向服务器端法出请求。
在客户端进行校验并没有错。但是,如果由于客户端提供了校验,而在服务器一端设计中认为来自客户端的输入是都是经过(客户端)验证的,从而可以直接使用的话,就有问题了。
这是因为,攻击者可以完全不需要客户端程序,而直接向服务器端发出数据提交请求。这种情况下,所有客户端的检查都可以被绕过。所以在服务器端的设计过程中,要总是假设来自用户的输入可以是恶意的,并提供相应的验证措施。
4.1.2验证输入的原则是确认输入的值都是有效字符,而不是试图拒绝输入的值中的无效字符。
初看起来,确认输入的值都是有效字符,和拒绝输入的值中的无效字符,这两个方案的是一回事。但是它们其实在实现上,以及对安全的影响上,有着很大的区别。
下面来举个例子说明。假设需要验证来自用户的UserAccount值是否合法,这里我们可以有两个选择。
选择一,确认UserAccount中的每个字符都是字母和数字。这种设计称为“whitelist”,也就是白名单设计。只有满足白名单条件的值才是合法的。我们可以采用正则表达式RegEx来验证。
using System;
using System.Text.RegularExpressions;
//验证UserAccount是否合法
public bool IsUserAccountValid(string strUserAccount)
{
// 验证是否字符仅仅是有大小写字母及数字构成
// 字符长度在1-20
if ( !Regex.IsMatch(userIDTxt.Text, @"^[a-zA-Z0-9]{1,40}$"))
{
return false;
}else {
return true;
}
}
选择二,确认UserAccount中没有非法字符。这种设计称为“blacklist”,也就是黑名单设计。不在黑名单上的值都是合法的。
黑名单设计的问题是:我们需要拒绝那些危险字符?例如,SQL语法中有下面这些字符需要特殊注意【3】:
特殊字符 |
SQL语句中的含义 |
; |
查询分隔符 |
\' |
数据分隔符. |
-- |
注释分隔符. |
/* ... */ |
注释分隔符. |
xp_ |
指示扩展的stored procedures,例如 xp_cmdshell. |
是不是只要检查用户输入中没有这些特殊字符就可以了?很可惜,并不是这么简单。首先,系统设计和实现的不同往往会导致某一种危险的输入值在黑名单上被遗漏。而且,攻击者往往可以通过“改头换面”的办法来绕过黑名单。常见的手段是有采用另一种编码方式,如采用%0x来编码等等。
所以在验证用户输入的开发过程中,强烈推荐采用白名单的设计方式来确认用户的输入是否是有效的。事实上,设计上采用白名单方式来验证用户的输入,不仅适用于防范SQL注入攻击,也是安全软件开发中用以防范其它攻击的有效手段。
4.2使用Stored Procedures和相应的参数传递
首先,并不是采用Stored Procedure和传递参数就能完全防止SQL注入攻击。例如,如果Stored Procedure设计成:
CREATE PROCEDURE dbo.RunQuery
@var ntext
AS
exec sp_executesql @var
GO
这个Stored Procedure在防范SQL注入攻击上完全没有任何作用。然而,合理的设计和使用Stored Procedure及参数类对阻止SQL注入攻击攻击是非常有效的。下面这段代码的功能同样是需要基于UserAccount来查询订单信息,但使用SqlParameterCollection来将UserAccount的值作为参数传递。
2
3using System.Data.SqlClient;
4
5
6
7public void QueryOrder(string strUserAccount)
8
9{
10
11… …
12
13 SqlConnection connection = new SqlConnection(connectionString);
14
15 DataSet userDataset = new DataSet();
16
17 SqlDataAdapter myCommand = new SqlDataAdapter(
18
19 "QueryOrderStoredProcedure", connection);
20
21 myCommand.SelectCommand.CommandType = CommandType.StoredProcedure;
22
23
24
25 myCommand.SelectCommand.Parameters.Add("@UserAccount", SqlDbType.VarChar, 20);
26
27 myCommand.SelectCommand.Parameters["@UserAccount"].Value = strUserAccount;
28
29 myCommand.Fill(userDataset);
30
31… …
32
33}
34
这里,UserAccount的值是作为字符串参数传递给对应的QueryOrderStoredProcedure,而不是简单的插入到构造的SQL查询语句中。参数在传递过程中会被检查类型和长度。如果检验失败的话,SqlParameter类会报告异常,从而有效的防范SQL注入攻击。
4.3数据库访问帐号的最小化权限
最小化权限的设计,同白名单一样,也是安全软件开发中的常用的设计原则。应用到SQL注入攻击环境下,意味着:应用程序用来访问数据库的帐号,所拥有的权限应该最小化。
需要强调的一点是,SQL注入攻击的根源在于Web开发中的不正确的代码。最小化权限的设计并不能替代修复代码本身错误的工作。它只是作为深层防御(Defense In Depth)中的缓解手段,其目的是为了尽可能降低SQL注入攻击的危害性。
例如,如果是通过Windows authentication机制来连接并访问数据库的话,用来访问数据库的Windows的帐号的权限就应该受到限制。一个非管理员权限的帐号,就可以阻止执行Fdisk类似级别的操作。在【4】给出了一个在Microsoft Windows 2003服务器,ASP.NET应用,后台SQLServer情况下,如何最小化ASP.NET应用 运行的Network Service帐号的权限的例子。有兴趣的读者可以参考【4】获取更详细的信息。
5. 总结
SQL注入攻击是最为常见的网站攻击的手段之一。开发人员在Web应用的设计和实现过程中需要意识到SQL注入攻击的危险,并在代码中采用相应的防范措施。
6. 参考文献
【1】 Mass SQL Injection Attack Targets Chinese Web Sites,http://news.yahoo.com/s/pcworld/20080519/tc_pcworld/146048;_ylt=AoZS0SbSq3tH.Cl1uEHJPMeDzdAF
【2】 Writing Secure and Hack Resistant Code Part 2,research.microsoft.com/collaboration/university/europe/events/dotnetcc/version4/Slides/leblanc2.ppt,Microsoft
【3】 SQL Injection, http://msdn.microsoft.com/en-us/library/ms161953.aspx, Microsoft
【4】 How To: Protect From SQL Injection in ASP.NET, http://msdn.microsoft.com/en-us/library/ms998271.aspx#paght000002_step2, Microsoft