在C#3.0中使用LINQ轻松防御SQL注入攻击



来源: ITPUB
  随着Internet逐渐普及,基于Web的各种非法攻击也不断涌现和升级,因此,很多开发人员被要求使他们的程序变得更安全可靠,这也逐渐成为这些开发人员共同面对的问题和责任。而很多使用Web系统的企业也不断投入大量的资金,以购买和使用更安全的框架和软件平台。很多开发人员也从一开始设计程序时就注重使用更有效的方法来建立安全的应用程序,并防止有破坏性的攻击。但不幸的是,开发团队的成员经常缺乏这方面的训练,以及进行安全设计的相关经验。

  当开发人员为应用程序加入更多的安全防护罩时,也许最先考虑的Web应用程序攻击就是我们众所周知的SQL注入攻击。这是一种通过命令注入方式进行攻击的方式。命令注入是任何通过在服务端运行非授权程序进行攻击的基本方法。这种方法一般是通过在客户端输入特殊或不可预料的字符串(在字符串中包含有破坏性的命令)来改变Web程序被期望的运行结果。由于这种攻击方式非常普遍,也非常容易掌握,因此,SQL注入攻击被大范围地使用,所以它是非常危险的,甚至是防不胜防的。但幸运的是,如果我们理解了SQL注入攻击的原理,是很容易防御的。

  随着微软的.NET技术的不断发展,也为开发人员提供了新的数据接口,如果适当使用它们,就可以完全防止SQL注入攻击。这项技术被称为集成查询语言,也就是LINQ(将在本文的后面进行讲解)。这项技术将和即将发布的Visual Studio2008以及.NET Framework 3.5一起发布。本文将带领读者探索如何利用LINQ来防止Web程序的SQL注入攻击,读者将会从本文深刻体会到LINQ在这方面的无限潜力。

  典型的SQL安全防护

  到现在为止我们已经看到在应用程序中开始一个SQL注入攻击并成功使用它是多么的容易。幸运的是,在上面已经提及过,SQL注入攻击很容易使用一些简单的手段防御。大多数有效的防御方法是在执行SQL语句之前验证应用程序中所有的被使用于数据访问的输入语句。我们可以在Web应用程序中直接来验证,也可以使用一些数据持久化组件,如Hibernate进行验证,当然,不管使用哪种方式,这种验证都需要在处理数据之前在服务端来验证类型、长度、格式和范围。但不幸的是,基于代码的验证方法并不十分简单,当遇到如下三种情况时,还会失败:

  1. 验证程序设计不完善。
  2. 验证只在客户端执行。
  3. 验证失败,即使在应用程序中只有一个单独的字段。

  还有别外一种防止SQL注入攻击的方法,就是在应用程序中参数化所有的SQL查询,无论是动态的SQL表达式,或存储过程。如果写出象下面的代码应该是很安全的:

SELECT [CustomerID], [CompanyName], [ContactName]
FROM [Customers]
WHERE CompanyName = @CompanyName
参数化的查询当执行SQL表达式时将输入看到是一个字符串值;因此,将这个值作为可执行的代码是不可能的。即使我们使用存储过程,也必须使用参数化输入,这是因为存储过程并不提供使内嵌查询防止SQL注入的功能。

  即便使用上述简单的防护措施,SQL注入仍然会成为许多组织的大问题。对于我们的开发团队来说,最大的挑战就是对所有的开发人员进行防御这种攻击的培训。甚至需要将这些标准和规范装订成册,以供开发人员随时查阅。而且这种方式为了使程序更安全,引入了很多的变量,这样可能会给我们带来更多的工作量。这就需要出现一种更方便和强大的防止SQL注入攻击的技术出现,这就是LINQ的由来。

  通过LINQ使数据访问更安全

  我们用LINQ to SQL进行数据访问时,它可以很容易地消除在我们的应用程序中由于SQL注入而带来的安全隐患,这主要是由于在LINQ中每一个执行的SQL查询都是参数化的。当使用LINQ建立SQL查询时,任何提供给查询的输入都被看做是参数字符串,无论这个输入是从哪里来的。开发人员可以在Visual Studio2008中使用集成的LINQ,并通过智能编辑器和编译时语法检查来书写正确的代码。编译器可以捕捉很多可能引起功能性错误或其他类似的安全隐患的查询错误。与之对比,我们写的SQL表达式只能在运行时通过数据库系统对其进行分析和解释。这样我们就很难在运行之前知道它们是否正确。曾经有很多攻击者试着通过对LINQ采取欺骗的手段使其运行非法或具有恶意的SQL语句。但幸运的是,最新的语言和编译器阻止了这一切的发生。

  心动不如行动,在这里我们来使用LINQ实现一个customer搜索的例子,并使用LINQ来阻止SQL注入的攻击。第一步是在数据库中建立和关系数据对应的对象模型。Visual Studio2008包括了一个新的对象关系设计器(也就是O/R设计器),它可以通过拖拽的方式为我们产生全部的对象模型,其中包括对象的设计和它们之间的关系。为了对我们的Northwind中的Customers表建立对象模型,我们需要通过选择“Add New Item...”,并选择“LINQ to SQL File”模板(这个模板将打开O/R设计器)在应用程序中建立一个LINQ to SQL文件。为了自动建立完整的Customers表的对象模型,在Server Explorer中选择这个表,并将它拖到O/R设计器的界面上,如图2所示。在这个例子中,O/R设计器在应用程序中加入一个Customers.designer.cs文件来定义我们在代码中使用的类。而不是直接写代码和数据库交互。

  在定义完Customers表的对象模型类后,我们可以通过customer数据查询而在代码中直接查询数据。在LINQtoSQL.aspx.cs的Page_Load方法中,实例化了被O/R设计器建立的CustomersDataContext类,它重用了在上面例子中SQLInjection.aspx而使用的连接字符串。下面是LINQ查询获得的一个Customer对象的集合的代码:
protected void Page_Load(object sender, EventArgs e)
{
string connectionString =ConfigurationManager.ConnectionStrings
["northwndConnectionString1"].ConnectionString;
CustomersDataContext db = new CustomersDataContext(connectionString);
GridView1.DataSource =from customer in db.Customers
where customer.CompanyName ==txtCompanyName.Text
orderby customer.CompanyName
select customer;
GridView1.DataBind();
}

  使用LINQ to SQL,如果我们提供"Oracle"作为Search的值,那么在运行时和在服务器执行的通过LINQ产生的SQL表达式的代码如下:

SELECT [t0].[CustomerID], [t0].[CompanyName],
[t0].[ContactName], [t0].[ContactTitle], [t0].[Address],
[t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Country],
[t0].[Phone], [t0].[Fax]
FROM [dbo].[Customers] AS [t0]
WHERE [t0].[CompanyName] = @p0
ORDER BY [t0].[CompanyName]}
  我们从上面的代码可以看出,WHERE子句被自动参数化了。因此,使用方便的SQL注入攻击是无法得逞的。不管用户在查询页的输入字段中输入什么值,查询总是类型安全的,而且在服务端不允许将输入字符串作为命令执行。如果我们使用上述的SQL注入攻击方法来浏览敏感数据,那么将什么也不会显示出来。

  小结

  从上面的例子可以看出,使用LINQ是非常容易在Web程序中预防SQL注入攻击的,当然,也非常容易检查出这种错误。微软的LINQ to SQL技术使用户通过对象模型和数据库进行交互而不是直接使用SQL和数据库进行交互,从而有效地避免了SQL注入攻击。这个LINQ结架被C#和Visual Basic建立来格式化字符串和SQL表达式,并阻止SQL注入攻击的发生,以使开发人员可以将更多的经理集中到程序本身的特性上来。无论我们选择使用LINQ或是SQL作为.NET应用程序访问数据的接口,或是设计自己的数据访问接口,我们都将会做出一个选择来建立更安全的应用程序。

posted on 2007-11-06 15:38  巍巍边疆  阅读(839)  评论(0编辑  收藏  举报