技术开发 频道

SQL注入安全威胁

  【IT168 技术文档】

  SQL 注入对于那些使用动态的SQL 数据库的系统是一个严重的威胁(也就

  是说,应用系统运行的时候,SQL 会被编译)。一个熟知SQL 技术的黑客很容易

  攻击这样防护薄弱的系统,但良好的应用程序设计可以减少风险。不要只是眼于

  满足纸上谈兵,设计人员就必须仔细考虑如何防护黑客可能对应用系统进行的攻

  击。此外,数据库管理员必须给软件开发者最小系统的权限。

  创建一个测试应用系统

  我们创建一个简单的测试应用系统可以帮你更好的理解SQL 注入是怎么实

  现的。这个系统是用SQL Server Northwind 数据库。为了简化创建应用系统,

  Employees 表是用来作为的用户表。用户的第一个名字和最后一个在名字都存在

  Employees 表。第一个名字和最后一个名字和实际的应用系统中输入的用户名和

  密码关联。用户可以登录时使用动态SQL ,一个参数化查询或存储过程。

  你可以下载SQL 语句ValidateUserProc.sql,或者得到应用系统的源代码

  injectSQL.cs 或者injectSQL.vb。

  虽然这个示例应用程序是一个Windows 应用程序,这可能是一个Web 应用

  程序,甚至是Java 应用程序。采用动态SQL 和完全独立于操作系统,数据库厂

  商,和编写应用程序的编程语言,造成的直接后果就是出现这样的缺陷。

  理解这个测试应用系统

  表面上,这个测试应用系统按照预期工作并符合基本的商业目标。通常的用

  户都会知道一个有效的登陆名和密码。那些不知道一个有效的登陆名和密码的是

  无法登陆系统的。实际上,应用程序将验证用户只能使用只有一个方法,要么符

  合用户名和密码,它不会提供另外的登陆方法。

  这个测试应用系统会提供一个选择不同的数据库来验证SQL 注入时候系统

  的脆弱性和如何防范它们。用动态SQL 验证用户身份为SQL 注入创建漏洞提供

  了机会,但参数查询和存储过程都防止SQL 注入。

  为了让应用系统简单,我们创建了一个单一的页面。在这个系统里输入一个

  有效的用户名和密码以后,用户当然会被带到另一个页面,因为测试应用系统所

  显示的用户“已被授权”。无效的名字和姓氏将导致应用程序显示“未经授权的”。

  无论是否使用动态SQL,存储过程,或一个参数查询,SQL 语句在测试应

  用程序执行都像下面这个例子:

  select EmployeeID from Employees

  where FirstName = 'Andrew' and LastName = 'Fuller'

  一个有效的名字和姓氏的结果EmployeeID 被送回从查询。无效的用户名和

  密码组合的结果在没有数据被返回。
 

【注】 51Testing授权IT168独家转载,未经明确的书面许可,任何人或单位不得对本文内容复制、转载或进行镜像,否则将追究法律责任。 本文出自51Testing电子杂志《51测试天地》第十四期,http://www.51testing.com/html/82/n-141082.html

  理解动态的SQL

  当一个程序在运行的时候将SQL 字符串也执行的话,它被称为动态SQL 。

  检查应用程序的代码没有提供明确的标志来区别哪些SQL 可以被实际执

  行,它只提供了一个标志这些语句可以被执行,只有SQL 事件探测器能找到那

  些实际执行的sql.。审查下列应用程序代码构建SQL 字符串的测试应用程序:

  .CommandText = "select EmployeeID from Employees where FirstName = '" + _

  FirstName.Text + "' and LastName = '" + LastName.Text +"'"

  上面的例子看起来SQL 的描述很符合逻辑,如果分别输入Andrew 和Fuller

  作为第一个名字和最后名字,然后SQL 事件探查表明,实际的SQL 执行的是:

  select EmployeeID from Employees

  where FirstName = 'Andrew' and LastName = 'Fuller'

  这是完全符合预定设计中的应用。无效的输入被发现,用户是无法获得权限。

  改变的逻辑威胁

  通过简单的文本框测试应用系统可以接受到输入的第一个名字和最后一个

  名字,用户可以自由地输入文字以外的其他名字或姓氏。黑客可以使用基本的

  SQL 知识就可以不通过猜测一个有效的名字和姓氏直接获得权限。

  用户输入下面的字符串,然后获得权限

  ' or 1=1—

  通过在第一名字文本框输入SQL 片段语句,一个黑客“注入”的SQL 片段,

  从而改变了SQL 语句的执行。注入的SQL 片段实际上由三个不同的片段,每一

  个不同的目的:

  1、在单引号关闭了First Name = '片段的模板查询。这样做是为了保持修改

  查询的语法的正确性。

  2、这个or 1=1 每次都会返回一个值,假设这个表不空。

  3、因为1=1 是永远返回一个true 的值,于是这个输入导致整个剩余的动态

  建立SQL 语句被忽视。任何投入的姓文本将被忽略。

  SQL 事件探查显示实际执行:

  select EmployeeID from Employees

  where FirstName = '' or 1=1--' and LastName = ''

  应用程序设计初衷是将名字与姓氏作为输入条件,但SQL 注入改变了SQL

  逻辑,使它们是多余的。

  多个声明的威胁

  未经授权的访问的权限的不同级别的严重程度取决于应用程序的目的。有时

  人们不认为这是潜在危害的安全威胁。

  例如,如果一个Web 应用程序提供收费进入公共平台系统,未经授权的访

  问导致收入的损失远远超过了加强安全功能的费用,毕竟,有很大一种可能,如

  果一个人进入了一个收费的公共服务系统,而要进一步进入这个网站存在失败的

  风险他就不会轻易尝试。然而,这样的推理是幼稚和致命的缺陷-如果在SQL 娴

  熟的黑客决定将注入新的SQL 语句呢?

  参考下面的SQL 代码:

  www.51testing.com

  《51 测试天地》第十四期15

  ' or 1=1;update Products set UnitPrice = 0—

  再次,在SQL 事件探查器显示实际执行的-其实,两个单独的SQL 语句:

  select EmployeeID from Employees

  where FirstName = '' or 1=1;

  update Products set UnitPrice = 0--' and LastName = ''

  分号是一个有效的SQL 字符用来联合多个SQL 语句。而在SQL 分析器里

  这个SQL 语句通过分号被逐个分开执行。

  黑客不仅限于增删改的操作(插入,更新,删除)删除一个表怎么样?假设

  具有删除这个应用系统表的权限,黑客可以使用删除这个表的语法来从数据库删

  除这个表,参考下面的输入:

  ' or 1=1;update prices set cost = 0;drop table audit_trail;shutdown--

  不仅仅是这个日志表被删除,就连数据库也会被立刻关闭。

  通过代码来预防

  为您的数据库提供最大的安全,您必须使用多种技术。第一道防线是预防在

  用户界面。

  只要您正在使用一个数据库,您必须先了解您的数据,因为只有你更好地理

  解您的数据,你才能更好地将能够保护它。在测试程序中Employees 表的

  FirstName 字段是被用做在用户表里的用户名字段。本字段最大长度为10 个字符,

  但测试程序并不限制用户的输入为10 个字符。这是一个令人震惊的缺陷。假如

  我们把限制输入10 个字符的话可以避免刚才我们举例的攻击事件。不过,并非

  所有的输入栏位很短,因此作为安全防卫,输入长度检查只是一部分。如果最大

  长度是20 个字符,我们只限制最多输入20 个字符吗?下面是一个例子,只需要

  19 个字符来关闭数据库:

  ' or 1=1;shutdown—

  假设我们把一些特殊的字符,比如分号,等于号,确认为无效的输入,然后

  用正则表达式验证来检查这些无效的字符和错误的输入。作为一套有效的输入标

  准的解决方案不仅仅限制这些字符的输入,也需要防护黑客通过SQL 的char 函

  数来使用一个独立的ASCII 来转化成字符来实现SQL 注入的输入.尽管在限制这

  些字符输入,但是这方法是相对还是很合适的, Visual Studio.NET 具有这样的

  一个正则表达式验证控制,极大地简化了在ASP.NET 网页中使用正则表达式。

  数据类型检查有助于发现错误的输入。用户界面往往接受日期,时间和数字

  输入的文本字段,虽然用户可以在文本框中输入任何他们想输入的,程序可以检

  查输入数据,看它是否是正确的数据类型。例如,如果一个输入文本框映射到

  EmployeeID 列,那么您应该检查任何用户输入,看它是否是整数型数据。只有

  在输入正确的数据类型将输入传递给数据库服务器进行处理。在数据类型的验证

  的时候,这些错误的输入会被提示失败。

  最根本的缺陷不是允许错误输入,而是这些错误的输入可以被执行。开发人

  员乐于用动态SQL,因为很方便,但是却没有办法在应用程序的设计阶段控制

  实际运行的SQL。

  通过存储过程来预防

  存储过程可以在创建的时候被编译,但是这些SQL 语句在创建的时候会被

  冻结住,参考上面的例子

  ' or 1=1—

  利用Stored Proc Login 按钮,SQL 事件探查器显示实际执行:

  exec ValidateUser @FirstName = N''' or 1=1--',@LastName = N''

  Where @FirstName contains the following characters: ' or 1=1—

  当FirstName 包含' or 1=1—这样的字符时,无论你输入的@FirstName 还是

  @LastName,存储过程将始终只有执行在创建时候所定义在存储过程中的

  SELECT 语句。SQL 语句的执行是不会因为输入而改变。此存储过程包含2 个

  输入,都是字符串,不管这些输入字符串包含什么,都会被总是当作字符串。即

  使一个分号被视为普通的输入字符,而不是SQL 语句分隔符。

  需要特别声明的是, 存储过程本身并不防范SQL 注入,也就是说如果存储

  过程本身使用动态SQL 的话它也可以SQL 注入。在一个取名为sp_excutesql 的

  存储过程中建立SQL 字符串在执行的时候与在应用程序中建立SQL 字符串在执

  行的时候一样易受攻击。有一点需要澄清的是:一个存储过程并不一定是易受

  SQL 注入的攻击和它的取名无关。这完全取决于用户怎样使用它。

  以下是SQL 事件探查表明在执行时有一个企图绕过身份验证,而使用一个

  参数化查询:

  exec sp_executesql

  N'select EmployeeID from Employees

  where FirstName = @FirstName and LastName = @LastName',

  N'@FirstName nvarchar(4000),@LastName nvarchar(4000)'

  , @FirstName = N''' or 1=1--'

  , @LastName = N''

  正如你所看到的,一个参数化查询是类似于存储过程,因为它处理的输入作

  为参数,而不是字符串,可以改变文字的查询字符串。如果您下载并审查代码示

  例,您将看到的代码,以参数化查询与代码调用一个存储过程是非常相似的。

  有一种误解,即认为动态SQL 是需要的。这不一定是正确的。参考下面这

  个例子,搜索页面的房地产信息列表。客户提出多个搜索条件(价格,卧室的数

  量,浴室等)。并不是每次每个搜索条件都被输入,所以这样的SQL 的where 条

  件可能会因此变化。动态SQL 往往使用在类似情况下。但是,您可以提高代码,

  构造一个动态SQL 语句在运行时建立一个参数化查询代替。当然还可以建立一

  个具有很多where 条件存储过程,这是因为,防范在一个参数化查询的时候SQL

  注入,存储过程是最安全办法。参数查询要求有权限操作对应用系统的表和视图,

  这是在安全数据上不被推荐的方法。

  当存储过程被执行的时候,它不需要具有任何表或者视图的权限的。

  在“Calling Stored Procedures from ADO.NET”中提到了在.NET 环境中调用

  存储程序。

  通过最小的权限来预防

  最基本的安全原则就是尽可能赋予最小的权限,用户只要具有完成操作任务

  最低的权限。客户在使用的时候不可以随便的具有一些操作数据库的权限,像增

  删改动表,关闭数据库等。当黑客企图删除一个表,但并没有权利这样做,就不

  会取得成功的。

  结论

  提高安全的非常好的做法就是可以防止数据库的权限的泄漏。使用forethought

  和精心设计的应用是在保护你的利益。虽然动态SQL 有其用途,就应该尽早作

  出决定,就以它是否将是最好的选择。如果可能的话,你应该考虑预存程序在设

  计早期阶段,因为他们的执行是不依赖用户输入的改变。你也应该彻底检查代码

  地看到,它不适合入侵。开发人员必须考虑像一个黑客,以便充分评估中的弱点

  及其应用。

0
相关文章