螺螺的blog
父母一天天老去了,我要努力!

每天,当我们打开bugtraq邮件列表,一个个的新漏洞不停的被发掘出来,让我们眼花缭乱:缓冲区溢出,Sql注入,XSS跨站脚本……,于是黑帽子们纷纷就这些公布的细节开始分析漏洞,编写攻击代码并测试之;白帽子们同样也要分析这些漏洞,并写漏洞扫描规则或者防范规则。黑帽子们的攻击代码很快就会在网上流传,脚本小子们也开始忙碌起来,他们不厌其烦的利用这些漏洞取得世界各地的具有漏洞的主机的控制权限,有时候病毒和蠕虫编写者也会来凑凑热闹,把攻击代码制作成病毒和蠕虫,在互联网上传播。

而我,作为一个应用开发工作者,似乎不那么关心这些网络安全事件,我们按照我们的规律同样在我们的圈子里面周而复始着,每天只想着,自己的这块进度能不能赶上,如何运用更好的方法论和模式来改善我们的开发过程。也许在我们看来,前面提到的那些人,不过是些义务代码审核人员。我们依旧会不假思索的把用户发送过来的字符串不加检查的拷贝到我们自己定义的变量,仍然会通过拼接字符串来构造我们的SQL查询语句来做我们的查询,也不会多考虑用户在页面里提交的内容是否会另外给我们的网页增加些特效。没有什么比能简单有效的完成自己的任务更重要的事情了,想的那么多简直是吃力不讨好的,甚至会让你为此而损失掉奖金或者领导对你的信任。

说了这么些,似乎已经背离了我想写这篇文章的主题,现在回到正题,就是安全漏洞,安全漏洞何以成为安全漏洞,透过那些纷繁复杂的技术分析,我们会发现漏洞大体都有共同的特征,第一种就是用户提交的数据被执行,第二种就是用户提交的数据导致程序流程的改变。这两个特征可以用来概括这些年来流行的主要漏洞类型,比如:缓冲区溢出漏洞就是因为用户提交的数据超长而覆盖了某些重要的指针引起程序流程的跳转从而导致用户提交的数据被执行,SQL注入漏洞的成因是把用户输入的字符串当作SQL命令来执行,XSS跨站脚本漏洞同样也是浏览器把其他用户提交的数据执行了。

于是我们发现解决安全漏洞的关键就在于阻止数据被当作代码执行,Windows 2003 Server的DEP数据执行保护就是一个很有针对性的解决方法;对于XSS漏洞,我们可以用HTML编码来把用户提交的要在浏览器显示的数据进行HTML编码,以阻止其在浏览器中执行;最后作为一个Web程序员,我稍多花点笔墨谈下SQL注入,在数据库编程中以下代码几乎已经成为一种经典的方式,在各种书籍教材里也不鲜见:

strSql = "SELECT * FROM [users] WHERE [uid]='" + strUid + "'";
execute(strSql);


这种方式就是通过拼接出来的SQL语句来执行查询,如果strUid是用户输入的话,这里就有了数据被当作代码执行的可能,很多文章都提出利用梳理用户输入的方式来阻止这类漏洞,下面例子把单引号过滤成T-SQL里的单引号的转义字符双单引号:

strSql = "SELECT * FROM [users] WHERE [uid]='" + strUid.replace("'""''"+ "'";
execute(strSql);


但是梳理用户输入并不符合我们的阻止数据执行的根本思想,要想用户输入的数据不被执行,我们只有彻底抛弃这种拼接SQL语句的方式,取而代之的应该是把用户输入作为参数传递给SQL语句,这也就是很多文章说用存储过程会更安全的原因,因为存储过程一般会把用户输入作为参数传递进去,可是存储过程也不是一定安全,如果你在存储过程内部进行了SQL语句拼接,就和上面的程序是一个道理了,见下面的例子:

CREATE PROCEDURE [dbo].[test]
(
    
@id int,
    
@username varchar(30)
)
AS
DECLARE @strSql varchar(300)
SET @strSql = 'SELECT * FROM [users] WHERE [id]=''' + str(@id+ ''' AND [username]=''' + str(@username+ ''''
EXEC @strSql
GO


这样的存储过程因为违反了上述的原则,所以同样是个有漏洞的存储过程。说到底,只要彻底杜绝拼接SQL语句,就可以彻底杜绝SQL注入漏洞,而有人会问普通的SQL查询怎么办,都用存储过程吗?不需要,java和.net都提供了参数式的SQL语句查询创建的方式,下面给个.NET的代码片断作为例子:

strUid = Request.QueryString["uid"].Trim();
strSql 
= "SELECT * FROM [users] WHERE [uid]=@uid";
sqlParameter 
= new SqlParameter("@uid", SqlDbType.VarChar);
sqlParameter.Value 
= strUid;
sqlCommand.Parameters.Add(sqlParameter);
sqlDataReader 
= sqlCommand.ExecuteReader();

 (注:java程序里请参考java.sql.PreparedStatement接口)

乍一看这里没有过滤输入好像有漏洞的样子,其实这里已经从根本上杜绝了注入的环境,不信你去试试看,:)从现在开始养成一个好的习惯吧。

西西,说是漫谈,结果谈到最后变成了防止SQL注入的古老话题,希望能对做相关开发的人一点启发,最终我们在BugTraq上面能少看见一些漏洞。这样似乎又有些厚此薄彼了,黑帽子朋友们不要拿鸡蛋砸我,同样我们也可以通过这样的特征来审核代码挖掘漏洞,当然对于这些东西,你肯定比我更在行,我就不班门弄斧了。

posted on 2006-01-19 03:13  luoluo  阅读(1043)  评论(3编辑  收藏  举报