扫一扫
关注微信公众号

SQL概述及在网络安全中的应用
2006-12-13   

1.网络应用和SQL注射

1.1概述

有些网络数据库没有过滤客户提供的数据中可能有害的字符,SQL注射就是利用插入有害字符进行攻击的技术。尽管非常容易防范,但因特网上仍然有惊人数量的存储系统容易受到这种攻击。这篇文章的目的是指导专业安全组织了解这种技术,并告诉他们正确的,用来防范SQL注射的办法,以及处理各种常见的,由于非法输入引起的问题.

1.2背景

在读这篇文章之前,你应该对数据库如何工作,以及SQL如何被用来访问数据库有一些基础的了解。我建议您阅读eXtropia.com的文章“Introduction to Databases for WebDevelopers”。(网址:http://www.extropia.com/tutorials/sql/toc.html)

1.3字符编码

在大多数的网络浏览器中,标点符号和许多其它符号在用于一个网络请求前需要把URL编码,以便被适当地编译(interpret)。在本文中的例子和截图中我使用了固定的ASCII字符以保证最大的可读性。然而,在实际应用中,你需要在HTTP请求中用%25来代替百分号(%),用%2B来代替加号(+)等等。


3.2.5 LIKE语句查询

另一个大的灾难是陷入一个LIKE子句的陷阱.(Seeing the LIKE keyword or percent signs cited in an error message are indications of this situation.)大多数的web搜索程序使用LIKE子句来查询数据库,比如下面这个:

SQLString = "SELECT FirstName, LastName, Title FROM Employees WHERE LastName LIKE '%" & strLastNameSearch & "%'"

这里面的%是通配符,在这个例子里,WHERE子句会返回TRUE,只要LASTNAME里有字符串含有strLastNameSearch.为了阻止SQL SERVER返回预计中的记录,你构造的SQL语句里必须含有LASTNAME里没有的字符串.web搜索程序搜索的字符串来自于用户的输入.通常有一个'和一个%在输入的字符串之前,因此我们构造字符串时,需要在WHERE子句中匹配它们.如果你提交了NULL作为搜索字符串,那么LIKE的参数会变成"%%",这是一个全匹配,会返回所有的记录.


3.2.6 “死胡同”

大部分的时候sql injection都要伴随着大量失败的实践,如果你发现你无论如何都不能插入相关的语句,并且无论你怎么做都不对,这个时候你就要判断自己是否掉进了一个死胡同,很多时候遇到这种情况你很可能是在一个多重嵌套的WHERE和SELECT子句的语句中,或者一些更加复杂的多重嵌套,连使用“;--”都没有用,所以自己要小心和避免在这种地方停留。

3.2.7 列的数目不匹配问题

我们可以从几次错误中得到很多有用的信息,并且加以调整自己的请求语句,这种信息多了,那就意味着我们离成功不远了。在猜列名时,如图所示,我们提交语句后会碰到以下错误“在UNION语句中的所有查询都必须在目标列表中具有相同数目的表达式”,这就是说你需要找出或者说是探测出在合法的请求中有多少个列。

这里我解释一下,UNION 语句是用来将两个不同的查询结果集相加得到一个结果集,UNION使用的唯一要求是两个查询的信息(你的查询语句)必须有相同的列数和相同的数据类型

我举个例子,web程序中有如下语句:

SQLstring= "SELECT FirstName,LastName,EmployeeID FROM Employees WHERE City ='"&strCity"'"

合法的SELECT语句和我们注入的UNION SELECT语句在WHERE子句中都要有相同的列。就上面的语句来说,如果我要加入UNION 语句的话,前后两者都要有3个列。并且他们列的数据类型也要相互匹配才可以。如果FirstName这个值是字符串类型的,那么在你注入的语句中所对应的值也应该是字符串类型的。一些数据库,如ORACLE,是对类型检查非常严格的。其他的数据库相对要好一些,允许你输入任何数据类型并且它会自动的把你输入错误的数据类型转换成正确的。比如SQL数据库中,你在varchar类型的地方输入数值类型的数据(如int)是不会报错的,因为在这里数值类型会被自动转为字符串类型。但是如果在smallint列处输入text类型则被认为是非法的,因为text类型不能被转换成int类型。把数值类型的数据转换成字符串型是被允许的,而反之则不行,所以默认都是使用数值类型的数据。
 

要想知道我们要注入的目标语句中有多少个列,你就要试探性的往UNION SELECT子句中添加相应的值,直到它不报“在UNION语句中的所有查询都必须在目标列表中具有相同数目的表达式”这样的错为止。如图所示,如果你遇到的是数据类型不匹配的错误,那么你要去改变列的数据类型。如果返回消息只是一个转换数据类型失败的错误,那就说明你已经猜对了列的数目,只是其中有个别的列的数据类型不对。那么接下来要做的就是判断是哪个列的数据类型的不正确导致的错误。然后将他改过来就可以了。

如果一切顺利,那么祝贺你,你会得到一个和上面格式类似的而且是合法的页面;无论动态页面在哪里出现,你都可以构造自己的语句应对自如。


3.2.8.WHERE关键字

报错为“无效的列名'EmployeeID'”,这个问题可能是由我们注入的语句结尾的WHERE关键字引起的,举例说明:

SQLString="SELECT FirstName,LastName,Title FROM Employees WHERE City='"&strcity&"'AND Country ='USA'"

如果我们注入的语句是UNION ALL SELECT OtherField FROM OtherTable WHERE 1=1 那么会得到如下的提交语句:

SELECT FirstName, LastName, Title FROM Employees WHERE City = 'NoSuchCity' UNION ALL SELECT OtherField FROM OtherTable WHERE 1=1 AND Country = 'USA'

这样就会报错:[Microsoft][ODBC SQL Server Driver][SQL Server]无效的列名 'Country'。

其实问题就是因为你注入的语句后,系统没有在从数据库的表中找到一个叫'Country'的列名。我们这里可以简单的用“;--”注释符号将其注释掉(如果我们是SQL Server)。或者干脆继续猜其他的列名,然后构造合法请求就如我们上一节讲到的一样。

表名的枚举

我们已经开始掌握如何来使用注入进行攻击,但是我们还要确定要从哪个表得到信息,换句话说就是我们要的到关键的表名才能获得我们想要的有用信息。如何获得表名呢?在SQL Server中,你可以很容易得从数据库中得到全部的表名和列名。但是在Oracle和Access中,你就不一定能如此轻易的得到了,这要看WEB程序对数据库的访问权限了。关键在于是否能得到系统建立时自动生成的表中包含的表名和列名。如在SQL Server中,它们分别为'sysobjects'和'syscolumns',(在本文最后我们将给出其他数据库系统自建表和相应的列名)我们用以下的句子可以在这些表中列出数据库的所有列名和表名,(根据情况自行修改):

SELECT name FROM sysobjects WHERE xtype = 'U'

这句话会返回数据库中用户定义的所有表,如果我们看到我们感兴趣的或者是想要看的表,那么我们就把他打开,这里以Orders为例构造语句:SELECT name FROM syscolumns WHERE id = (SELECT id FROM sysobjects WHERE name = 'Orders')得到结果如图。

3.2.10.单一纪录

上面我们构造的语句返回了大量的信息,如果你只想显示一条数据纪录也是可以的。你完全可以构造你的注入语句来得到你想要的唯一的信息。我们只要在WHERE子句中添加关键字来避免某些行的关键字被选中就可以了。我来举个列子:' UNION ALL SELECT name, FieldTwo, FieldThree FROM TableOne WHERE ''='

我们这样就可以得到FieldOne,FieldTwo和FieldThree的第一个值,假设我们的到的分别是"Alpha", "Beta"和"Delta"。注意,更有意思的来了,我们要得到第2行的值,怎么构造下面的语句呢?这样来:' UNION ALL SELECT FieldOne, FieldTwo, FieldThree FROM TableOne WHERE FieldOne NOT IN ('Alpha') AND FieldTwo NOT IN ('Beta') AND FieldThree NOT IN ('Delta') AND ''='

这里有一个子句“NOT IN VALUES”,它的作用是不再返回我们已经得到的信息,即不是alpha,不是beta,不是delta.既然都不是,数据库就会傻乎乎的告诉我们第二行的值。我们再假设我们得到第二行的值为"AlphaAlpha", "BetaBeta"和"DeltaDelta"。

我们来获得第三行的值,构造语句如下:' UNION ALL SELECT FieldOne, FieldTwo, FieldThree FROM TableOne WHERE FieldOne NOT IN ('Alpha', 'AlphaAlpha') AND FieldTwo NOT IN ('Beta', 'BetaBeta') AND FieldThree NOT IN ('Delta', 'DeltaDelta') AND ''='

这样就避免了得到第一次和第二次我们已经得到的值,我们就这样试下去会得到数据库中所有的值。这看起来好像确实比较麻烦,但在这里却是最有效的,不是么?
 
3.3 插入

3.3.1 插入基础

关键字INSERT 被用于向数据库添加信息,通常使用INSERT主要在包括用户注册,论坛,添加商品到购物车,等等。检查INSERT使用的弱点和检查WHERE一样。你可能不想使用INSERT,如何避免被利用弱点是一个重要的考虑问题。INSERT注入尝试常常会让数据库以行形式返回结果导致泛滥的单独的引用和SQL关健字的意义可能改变.壬能的过滤象s/[^0-9a-zA-Z]//g 这样的特殊字符。可能的时候尽量使用数字,在这以后只使用数字和字母。如果你需要包括各种各样的标志或标点。确信完全的把它们转换成html标记,像“"e;" or ">”。例如,一个用户提交了一个email地址只允许使用数字和字母还有"@", "_", "." 和"-"。仅仅只有这些字符可以转换成html标记。

4.2. 编写安全的web程序

这里同样有很少的特殊的sql注入规则。First, prepend and append a quote to all user input。

尽管数据使数字。其次,限制网页应用程序的数据库用户在数据库里的权限。不要给这个用户访问所有的存储过程的权利如果这个用户只需要访问一些预定义的。

这部分包括了所有在sql注入中有用的系统表,你可以在google上搜索到每一个的表的列的定义

5.1. MS SQL Server

Sysobjects

syscolumns

5.2. MS Access Server

MSysACEs

MSysObjects

MSysQueries

MSysRelationships

5.3. Oracle

SYS.USER_OBJECTS

SYS.TAB SYS.USER_TABLES

SYS.USER_VIEWS SYS.ALL_TABLE

S SYS.USER_TAB_COLUMNS

SYS.USER_CONSTRAINTS SYS.USER_TRIGGERS

SYS.USER_CATALOG


热词搜索:

上一篇:Oracle8的不安全因素及几点说明
下一篇:关于SQL SERVER的一些安全问题

分享到: 收藏