Godaddy服务器上关于ASP.NET网站建设一些经验 - 防SQL注入攻击(三)

作者: 阙荣文 ( querw )

什么是SQL注入攻击,有什么危害

先来看一个例子说说SQL注入攻击是怎么回事,有什么危害.

在有用户参与的网站中,所有操作中最重要的就是登录.要求用户输入用户名和密码,然后在数据库里校验输入的有效性.
我相信很多人(反正我以前一直就是怎么写的)写类似下面的代码:


(代码1)
string strUserName = txtUserName.Text;
string strPwd = txtPwd.Text;
string strSql = "select * from UserTable where username = ";
strSql += "'" + strUserName "'";
strSql += " and password ='" + strPwd + "'";
...

像这样依赖用户输入来"拼凑"SQL语句的代码是极其脆弱的. 假设一个用户(也许是黑客)输入的密码是 ' or '1'='1 用户名无所谓什么字符,看看这条SQL语句会变成什么:

select * from UserTable where username = 'what ever you input' and password = '' or '1'='1'


执行这个语句会返回整个UserTable表,再加上很多网站的管理账号的用户名就是 admin, 那么悲剧的发生就无法避免了,入侵者很容易就以管理员的身份登录了,不管你的密码设置得多长,多复杂. 事实上入侵者能够利用上述的漏洞获取到远远比你想象的多的信息,如果他精通SQL的话,整个网站都有可能变成他的玩具. 所谓SQL注入攻击,大概就是这个意思了.SQL注入攻击使用正常的WEB浏览工具,防火墙对此无能为力,而且现成的攻击工具很多,使用也简单.如果网站存在这样的漏洞的话,任何一个人都有可能轻松攻破你的网站.

如何防范?
在网上随便搜一下,就可以得到 "通用防注入" 代码,它的原理是检查访问者提交的(包括使用GET 和 POST方法)数据,如果内容里含有某些SQL敏感的单词,则采取相应的防范措施. 比如某个"通用防注入"的关键字字典是: '|exec|insert|select|delete|update|count|chr|truncate|char|declare|--|script|*|char|set|(|) 所有提交的内容中含有上述任何一个单词,都被认为是攻击尝试.

不可否认这是一个解决方案,但我认为这只是治标不治本,理由:
1. 打击面过大,比如博客/新闻网站,用户的提交包含上面的单词是很正常的,但是如果过滤的话会被认为是入侵行为.
2. 没有从根本上解决问题,黑客技术的发展非常快,所谓道高一尺,魔高一丈.这个关键字字典也许永远都没办法包含所有危险的关键字.

解决问题要找根本,SQL注入漏洞的根源在于直接用访问者的输入 "拼凑" SQL语句并执行.那就不要拼凑SQL语句了 - 使用参数或者存储过程. 通过参数或者存储过程,SQL语句受开发者/管理员控制,用户的输入被限制在特定作用范围,就好像被笼子关着的野兽,牙齿再尖利,也咬不到笼子外的东西.

(代码2)
string strSql = "select * from UserTable where UserName = @UserName and Password = @Password";
SqlParameter[] param = new SqlParameter[]
{
      new SqlParameter("@UserName", strName),
      new SqlParameter("@Password", strPwd)
};
DataSet ds = db.OpenDataSetS(strSql, param);
...

现在,不管入侵者提交什么用户名或者密码,都被作为 @UserName 和 @Password 的值传给数据库引擎,而不在影响SQL语句本身.

另外提一句,适当做一些过滤总是有好处的. 比如这么一个链接 http://xxxx/userdetail.aspx?userid=100001
服务器页面会用Request的QueryString方法(或者类似的方法,本文的写作背景是ASP.NET / C# / SQL Server 2005)获取userid的值,如果直接把userid作为SQL语句的一部分,那么就会造成一个SQL注入漏洞. 但是这里很明显,userid是一个数值,如果使用前总是先把字符串转成数值型,那么就可以避免攻击.还有很多类似的地方,比如一般都有规定用户名的最长字符数,再接到访问者输入时可以检查一下. 总而言之,在逻辑上做一些检测会使程序更加严谨,错误可控.

后记
笔者的网站就经历过SQL注入攻击,之前全部采用"拼凑"SQL语句的方式写代码,给了我一个惨痛的教训.网站被安装shell,重要邮箱被盗(邮箱密码和网站admin密码设置成一样了,另一个惨痛教训.)
域名被修改.(域名密码和admin密码也设置成一样了,捶胸顿足...) 还好,最后"黑客"同志看在我们都是同胞弟兄的份上,把密码都换给我了. 被狠狠的上了一课. 感谢他的好心,如果碰到一个有恶意的攻击者...无法想象.

另外,我在网上搜索发现一种说法:使用参数并不能绝对杜绝SQL注入攻击,不过也没说出个所以然.我也不是很明白,如果您知道,能否告诉我一下([email protected])?非常感谢.

附录: 代码2中OpenDataSetS()函数


    public System.Data.DataSet OpenDataSetS(string strSql, SqlParameter[] param)
    {
        // 判断连接字符串是否为空.
        if (!Open()) return null;

        DataSet ds = null;
        SqlDataAdapter da = new SqlDataAdapter();
        SqlCommand cmd = new SqlCommand();
        cmd.Connection = " *** my sql connection string ***";
        cmd.CommandType = CommandType.Text;
        cmd.CommandText = strSql;

          if (param != null)
          {
              foreach (SqlParameter p in param)
              {
                  cmd.Parameters.Add(p);
              }
          }
        da.SelectCommand = cmd;

        ds = new DataSet();
        da.Fill(ds);

        cmd.Dispose(); cmd = null;
        da.Dispose(); da = null;
        return ds;
    }

你可能感兴趣的:(sql,String,服务器,null,asp.net,dataset)