前置知识:数据库、SQL语句、Web编程(ASP、ASP.NET)

前言:这次要谈到问题,其实不是什么新鲜的内容了,早在许多年前,就在各种黑客杂志和网站上提及过。这次我重新谈起这个问题,一方面是为了完成作业,另一方面我会结合目前的前沿技术来谈谈在SQL注入方面的新进展和防范。希望大家能以技术学习为目的,不要利用SQL注入漏洞进行非法攻击。

一、SQL注入漏洞是如何产生的


我们知道,一个动态网站,往往是要和数据库紧密交互的,即按条件从数据库中取出数据在网页上送显,或按用户的操作去改变数据库中的内容。最普遍的应该是新闻系统。我们经常在地址栏看形如这样的URL:http://某网站/ReadNews.asp?id=32,这个URL的作用是把ID=32传给 ReadNews.asp,ReadNews接受到这个id后,就会到数据库中找到id为32的新闻,取出这条记录并将它显示在网页上。然而,这样真的安全吗?

我们知道,一般,新闻表的ID,是一个int型的主键,注意,我强调的是int型。所以,这个参数应该严格限制为 int,而ASP/ASP.NET中,甚至其他编程语言中,QueryString一定是string型,如果程序中不加处理,就意味着,黑客可以构造特殊的参数来达到获取和修改额外信息的目的。

即:SQL注入的原理,就是从客户端提交特殊的代码,从而收集程序及服务器的信息,从而获取你想到得到的资料。

二、SQL注入的一般步骤


1. 探查SQL注入地址

找一个带QureyString的地址,例如http://www.某某.com/shownews.asp?id=32,在这个地址后加一个单引号’。看看发生了什么:

Microsoft JET Database Engine 错误 ‘80040e14‘
字符串的语法错误 在查询表达式 ‘ID=32‘‘ 中。
/ shownews.asp,行8

JET Database表示网站使用的是Access数据库,并且,程序没有限制QueryString中的字符串。

其实,最终执行的SQL语句是类似这样的:SELECT * FROM News where ID=32’,后面多了个引号,当然就报错了。

当然,有些网站不会返回错误或者程序员已经屏蔽了单引号,所以我们必须换一种更精确的测试方法:

试着访问这3个URL:

http://www.某某.com/shownews.asp?id=32   & nbsp;       正常显示
http://www.某某.com/shownews.asp?id=32 and 1=1    正常显示
http://www.某某.com/shownews.asp?id=32 and 1=2    报错

我们来看看程序都执行了什么:

SELECT * FROM News where id=32;                   id=32为真
SELECT * FROM News where id=32 and 1=1;            id=32为真,1=1永真
SELECT * FROM News where id=32 and 1=2; 1=2         id=32为真,1=2永假,真and假=假。

这样我们就能知道这个url是否存在注入漏洞了。这个例子中,是存在漏洞的。

2. 开始攻击!

进一步改造URL:

http://www.某某.com/shownews.asp?id=32 and 0<>(select count(*) from admin)

如果系统存在admin表,那我们就得到了网站的管理员密码了。

当然,在攻击一个网站时,我们不知道它的表结构。于是只能用字典枚举法来猜测。这也就是一些SQL注入攻击器的原理,猜解一些惯用的名字。比如user、admin之类的。

再邪恶点,我们可以远程执行cmd指令:

http://www.某某.com/shownews.asp?id=32& amp;#8217;; declare @a sysname set @a=’xp_’+’cmdshell’ exec ‘命令语句’---&aid=9

这样你就可以用cmd命令去控制服务器,修改管理员密码、创建用户等,甚至格式化他的硬盘。

3. 不知道用户名密码,利用SQL注入,登录系统

这个真是太邪恶了,用户名:’or’’=’  密码:’or’’=’。猜猜系统做了啥?

SELECT * FROM Users Where Username=’’or’’=’’ and Password=’’or’’=’’

于是,系统返回了Users中所有的记录……

其实SQL攻击还有很多种,我不一一介绍了,太邪恶了。

三、防范


对于我们开发者来说,最重要的不是黑别人,而是防止被黑。我本人在这方面稍有经验,下面介绍一下防御的思路以及具体代码:

1. 在程序中进行过滤

首先,针对SQL注入漏洞产生的原因,我们首先想到的,应该是严格限制用户提交的字符串。也就是URL之后的QueryString,当然,也包括Form、Cookie等。因此,我们需要在接受这些用户传值的页面对参数进行过滤。

考虑到ASP已穷途末路,下面所有的例子都以ASP.NET(C#)为例:

ReadNews.aspx.cs:

public void ShowNews()
{
    int newsId = Convert.ToInt32(Request.QueryString[“id”]);
    //……
}

我们声明了一个int型的newsId,因此,它只能接受整数类型的参数。一旦URL中,ID=的后面不是整数,就会报错。当然,这种错误是可以捕获的,有必要的话,你可以try…catch一下,或者让.NET自己的代码监视器来完成。

但是,我们接受的参数,不一定只有一个,或者必须是string,怎么办呢?这时候我们需要构造一个复杂的函数,用于过滤非法字符(主要是单引号和分号)。我建议大家把它写在公共类中,调用方便,一劳永逸。

public static string StringFilter(string str)
{
    if (!string.IsNullOrEmpty(str))
    {
        str = str.Trim().Replace("‘", "");
        str = str.Trim().Replace(";", "");
        return str;
    }
    else
    {
        return "";
    }
}

2. 使用存储过程

在.NET下最好的办法是使用存储过程!当然,只有SQL Server可以这么做。

存储过程让用户对数据库的操作只能以特定的存储过程实现,不是单纯的Execute一条SQL语句,因此很好的防范了黑客构造奇奇怪怪的SQL语句进行注入攻击。

3. 对用户密码进行加密

如果黑客攻破了外层防御,那么数据库中加密的内容将是最后一道防线。用户表中密码字段储存的数据,应当采用不可逆加密算法进行加密。比如MD5算法。当然,一次MD5的结果不是最安全,有很多网站有在线查询MD5的服务。我推荐的做法是,以MD5为基本加密方式,在字符串中再做处理,比如这样:

 

上面这种算法挺简单,但已经是非常强的加密了。黑客几乎无法通过枚举、反查来找出密码。

存入数据库时,采用以上算法进行加密。用户登录时,用以上算法对用户输入的密码加密,然后和数据库中的加密结果对比,如果一致则登录成功。

四、总结


1. 使用SQL Server、Access的系统都可能存在SQL注入漏洞,尤其是ASP+Access的程序,因为ASP是一种不严格的语言。

2. 牢牢记住,在执行SQL语句之前,对参数做必要的检查,每个带参数的函数,都必须对参数合法性进行验证!

3. 如果你采用ASP.NET+SQL Server开发系统,那么使用存储过程是最好的选择。

4. 对敏感信息加密保存,选择正确的加密方式(可逆/不可逆)

五、写在最后


感谢读到这里的读者,希望本文对你们有帮助。本人水平有限,如果你有什么更好的建议,欢迎和我交流。我的邮箱:edi.wang@outlook.com 另外,到现在为止,我网站的访问量已经达到了68662,感谢所有支持我的朋友们。