SQL注入是常见的网络攻击方法,之所以能够实现,是因为网页有着SQL漏洞。我们可以利用存在的漏洞来获取我们想要的各种信息。
1. 对SQL注入的理解
所谓的SQL注入,是利用了网页的地址,即URL。首先URL必须是动态网页,也就是能够通过网址进行参数传递,如’?id=1’;静态网页因为没有参数传递,所以不存在SQL注入的概念。
通常动态网页会涉及到信息的输入,例如网站登录时ID和密码的验证过程实际就是通过网址对数据库进行参数传递,首先验证信息,然后从数据库获取并返回数据。通过网页参数传递的过程,实际是一个通过前端(浏览器)向数据库进行数据获取的过程,这个过程实际上是通过SQL语句实现的,这个过程通过传递不同的参数,向数据库进行验证或者获得想要的数据。但前端是无法看到传递的SQL语言是什么,我们只能在网页地址中看到传递的参数是什么。
这个参数就像一个接口,前端通过它与向端数据库发送指令,即SQL语句。SQL注入利用的就是这个接口(如?id=xxx),这里的id就是参数,在SQL语句中它是一个列属性,数据表的这一列记录的是不同的id。那么究竟SQL注入是什么呢:我们知道通过网址传递的是一个参数(类似于程序语言中的变量),通过赋予它不同的值来获取数据库中对应的数据,如:Select
username,password from information_schema.columns where
id=‘1’;这个语句,假设通过屏显返回的数据就是用户名和密码,那么在前端向服务器进行请求的过程中,参数’1’是根据我们输入的内容而变化的,在向数据库服务器请求数据时,SQL语句结构并不发生变化,只有参数id是根据前端输入改变的一个变量。
也就是说,在网址中进行参数传递,其实是执行一个只有参数变化的SQL查询语句,我们只能通过浏览器向后端传递参数,既然SQL语句是存在于后台的,我们无法控制查询的数据类型(如数据库名、表名、列名、参数为任意值时对应的其它属性信息等),我们知道如果要执行各种我们感兴趣的查询,就要使用SQL语句,但如何向后端传递我们编写的SQL语句呢?
已知参数是在URL中通过?xx=xxx进行传递的,也就是说我们只能向服务器输入特定类型的参数(由后端封装),如果我们能想办法通过传参这个接口,将写好的SQL语句传给数据库,那么就达到了侵入数据库获取信息的目的,也就是SQL注入。假设我们输入参数后后端执行的查询语句依然是Select
username,password from information_schema.columns where id='1’
,我们都知道SQL语句可以进行联合查询,这里讲述原理就使用联合查询进行举例。 假设我们能够在这个语句的基础上,插入一段union
select xxxxxxxx的话,就能够让数据库执行后半句select语句并返回我们想要的信息。
2.SQL联合注入的过程
这里存在两个问题:首先,我们如何将这个语句通过浏览器传递给后端呢?首先我们要猜语句,就是通过向浏览器中输入一些无法识别的参数,如’,),"等(将这些符号作为参数时系统会返回语句错误信息),从语句错误信息中,我们就可以分析出参数在语句中是如何被包装的。因为参数可以有多种包装形式,如id=1,id=‘1’,id=(1),id=(‘1’),id=(“1”)都是正确的,所以我们需要从页面返回的语句错误信息来判别参数时如何被封装的。如我们在URL中的传参部分输入id=1’,这时系统返回错误语句
you have an error …near’‘1’’ LIMIT 0,1’ at line1
这里要重点分析的就是near后面的语句,注意观察1周围的几个上引号,左边第一个上引号与 LIMIT 0,1后面的上引号是一对,用来强调内容,那么错误语句中的参数1就变成了’1’’,也就是说SQL语句执行的参数是id=‘1’’,显然是因为在1后多输入了一个’造成的,这时我们就分析出了前端输入的参数在SQL语句中是通过两个单引号’_'进行包装的。
分析这些做什么用?我们都知道原本前端输入的参数是数字,后端执行时会给参数加上’_’,这时语句就结束了,如果我们在后面写一个union语句,就可以获得想要的数据,但这里有一个问题,前端默认传递的是一个参数,所以会在传参最后加一个单引号,如果我们加了一个union语句,就会造成Union前的select语句中的参数id只有左边一个单引号,这时如果我们在前端对参数id=1补充一个’,再插入union语句,就可以使得前半句顺利的执行,后半句入侵语句也可以执行。
到这里SQL注入的基本原理就清楚了,其实id=后面不论写什么(数字,字符串,语句),传递到后端后都会为其加上一对单引号变成’xxxxxxxxxxxxxx’,在这里输入的一对东西都被系统当做是参数,这里在1后加单引号的目的实际上就是把服务器为参数加的一对单引号打断,这个结果叫做闭合,即union前的语句可以顺利执行(没有语法错误),那么后半句无论我们要查询什么,系统都会执行。这里的例子是参数被单引号封装的情况,其它情况同样类似,通过返回的错误信息进行分析,相关的教程网上很齐全,不再赘述。
通过上面的分析我们知道,通过闭合以及Union查询,系统不但执行了原本的传参语句,还执行了我们自己编写的select语句,这种技巧实际上就是一种语句的拼接,如果服务器没有对参数进行相关的过滤,就会造成SQL漏洞,使得注入发生。这时union前后的两个select语句都被执行,会通过前端返回的窗口来进行显示,而后半句才是我关心的信息,如何能让后半句被执行而前半句不被执行呢?这里需要注意,只能通过修改前半句的参数,如把参数1变成1’,或1xxx,或任意数据库中肯定不存在的参数,那么前半句就会返回空值,只显示后半句的查询结果。
需要特别注意的是:只能对参数进行修改,而不能造成后端查询语句的语法错误(通过一个不存在的参数查询数据和语法错误是两个概念),也就是说虽然参数是不存在的,但语句的格式不能变,不能有任何的语法错误。
例如:Select username,password from information_schema.columns where
id=‘1’’ union select table_name from information_schema.tables where
table_schema=‘security’;
这个语句通过错误参数1’将前半句查询语句失效(实际上是返回空值,并不是语句没有被执行),就可以在前端显示我们写入的SQL语句了。在学习SQL语言时我们知道,union语句前后的两个select必须具有相同个数的参数,否则就是语法错误,在注入时也一样,虽然我们失效掉前半句语句,但我们写的select语句的参数个数仍要保持与前半句一致,否则就会造成语法错误,具体的过程待后续进行补充。
到这里Union注入的原理就说完了,实际上这只是一种注入方式,叫做爆破,我们可以写不同的语句对数据库、表、列等进行爆破,获得想要的信息。