不倒翁之SQL注入认识

今天看到一篇很好的文章,所以就总结下来了。。。。。。

0x00 【实例引入】
以PHP+ MySQL为例,一一个web网站中最基本的用户系统来做实例演示,一起来看看SQl注入的发生过程。
1、创建一个demo的数据库:
CREATE DATABASE  `demo` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;<span style="font-size:12px;"><span style="font-family:Monaco, Menlo, Consolas, 'Courier New', monospace;"><span style="color:#333333;"></span></span></span>

2、创建一个名为users的数据表,并向其中插入两条测试数据:

CREATE TABLE  `demo`.`user` (
`uid` INT( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT  '用户uid',
`username` VARCHAR( 20 ) NOT NULL COMMENT  '用户名',
`password` VARCHAR( 32 ) NOT NULL COMMENT  '用户密码'
) ENGINE = INNODB;
INSERT INTO `demo`.`user` (`uid`, `username`, `password`) VALUES ('1', 'zheng', MD5('123456'));
INSERT INTO `demo`.`user` (`uid`, `username`, `password`) VALUES ('1', 'chen', MD5('12345678'));

demo1
通过传入的username参数,在页面打印出这个会员的详细信息,编写userinfo.php程序代码:

<?php
header('Content-type:text/html; charset=UTF-8');
$username = isset($_GET['username']) ? $_GET['username'] : '';
$userinfo = array();
if($username){
       //使用mysqli驱动连接demo数据库
        $mysqli = new mysqli("localhost", "root", "root", 'demo');
     $sql = "SELECT uid,username FROM user WHERE username='{$username}'";
    //mysqli multi_query 支持执行多条MySQL语句
   $query = $mysqli->multi_query($sql);
 if($query){
            do {
                 $result = $mysqli->store_result();
                 while($row = $result->fetch_assoc()){
                         $userinfo[] = $row;
                   }
                 if(!$mysqli->more_results()){
                               break;
                       }
         } while ($mysqli->next_result());
   }
}
echo '<pre>',print_r($userinfo, 1),'</pre>';

假设我们通过 http://localhost/test/userinfo.php?username=zheng 这个url来访问网站数据库资源,正常强狂下,带入数据库执行的一句是:
SELECT uid,username FROM user WHERE username='zheng' 
但是,如果用户在浏览器传入的username的参数是zheng';show tables -- ,即url变为:
http://localhost/test/userinfo.php?username='zheng';show tables -- '。此时实际执行的SQL语句是:
SELECT uid,username FROM user WHERE username='zheng';show tables -- '
这样就导致了原本想要执行查询会员详情的SQL语句还额外执行了show tables; 语句,此时浏览器端就会显示开发者不希望的结果。

demo2
模拟web网站的用户登录系统login.php,代码如下:

<?php
if($_POST){
        $link = mysql_connect("localhost", "root", "root");
     mysql_select_db('demo', $link);
 $username = empty($_POST['username']) ? '' : $_POST['username'];
       $password = empty($_POST['password']) ? '' : $_POST['password'];
       $md5password = md5($password);
        $sql = "SELECT uid,username FROM user WHERE username='{$username}' AND password='{$md5password}'";
      $query = mysql_query($sql, $link);
      $userinfo = mysql_fetch_array($query, MYSQL_ASSOC);
   if(!empty($userinfo)){
            //登录成功,打印出会员信息
               echo '<pre>',print_r($userinfo, 1),'</pre>';
   } else {
             echo "用户名不存在或密码错误!";
     }
}
?>
<!DOCTYPE html>
<html>
<head>
      <meta charset="utf-8">
  <title>Web登录系统SQL注入实例</title>
</head>
<body>
    <form name="LOGIN_FORM" method="post" action="">
        登录帐号: <input type="text" name="username" value="" size=30 /><br /><br />
        登录密码: <input type="text" name="password" value="" size=30 /><br /><br />
        <input type="submit" value="登录" />
  </form>
</body>
</html>

此时输入正确的用户名zheng和密码123456,执行SQL语句为:
SELECT uid,username FROM user WHERE username='zheng' AND password='e10adc3949ba59abbe56e057f20f883e'
若将输入的用户名换成 zheng' or 1=1 -- ,密码随意,如aaaaaa,那么拼接之后的SQL语句就变成:
SELECT uid,username FROM user WHERE username='plhwin' or 1=1-- ' AND password='0b4e7a0e5fe84ad35fb5f95b9ceeac79'
意味着,攻击者不需要知道用户真正的用户名和密码就可以登录系统。

0x01 【SQL注入初级】
1、什么是SQL注入?
     用户提交的数据代入数据库查询
2、SQL注入都有哪些类型?
分类:根据注入去数据的方式不一样来分类(按获取数据的速度排列)
     (1)延时注入
               sleep(秒) if (true,sleep(1),0)
     (2)盲注(不需要显示位,不需要代码输出mysql错误信息)
               (and 1=1 and 1=2)and hex(substr(data,1,1))>0      
               利用函数:
               exists():判断查询结果是否存在 存在为1(True),否则是0(False)
               ascii():查询的字符串最左边的字母的ascii码。如果str是空字符串,返回0,。如果str是NULL,返回NULL
               substr( string string,num start,num length):返回一个字符串的子串,参数有:字符串,偏移开始位,偏移长度。mysql中的start是从1开始的。
     (3)报错注入(利用数据库的错误消息来进行注入,需要mysql_error()开启php报错信息 )
          各类数据库条件:
               mysql(64个字符) group by x
               oracle(大概2000个字符左右)xmltype
               mssql(1前多还是2千多字符) 数字转换错误
           语法格式:
               <1> and(select 1 from(select count(*),concat((select (select ( payload ))) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)
               <2> extractvalue(有长度限制,最长32位)
               <3> UpdateXml(有长度限制,最长32位)
               <4> NAME_CONST(适用于低版本) 
      (4)union注入(需要显示位)
3、SQL注入能干什么
     通过SQL注入攻击实现数据窃取和数据篡改。
    1.获取数据库数据(曾删改查)
    2.读写文件
    3.执行操作系统命令

4、如何判断是否存在注入
     数字型、字符型、搜索型
     报错信息
数字型:
     sql="select * from news where id=1 and 1=1";
     sql="select * from news where id=1 order by 1";
字符型:
     1' and '1'='1
     sql="select * from news where type='1' and '1'='1'";
     sql="select * from news where type='1' order by 1'";
搜索型:
     %' and '%'='
     sql="select * from news where title like '%%'"

5、传说中的万能密码
admin' or 'a'='a
admin' or 1=1#(mysql)
admin' or 1=1--(sqlserver)
admin' or 1=1;--(sqlserver)

0x02 【如何防御SQL注入】
1、关闭错误信息提示功能
2、检查变量数据类型和格式
  只要是由固定格式类型的变量,在SQL语句执行前,应该严格按照固定格式检查(数据太大的情况下不适合)
3、过滤特殊符号
  同常采用addslashes函数,他会在指定的预定义字符前添加反斜杠转义,预定义字符:
单引号 (') 双引号 (") 反斜杠 (\) NULL。
来看两条例子:

$uid = isset($_GET['uid']) ? $_GET['uid'] : 0;
$uid = addslashes(uid);
$sql = "SELECT uid,username FROM user WHERE <span style="background-color: rgb(255, 204, 204);">uid='{$uid}'</span>";
$uid = isset($_GET['uid']) ? $_GET['uid'] : 0;
$uid = addslashes(uid);
$sql = "SELECT uid,username FROM user WHERE <span style="background-color: rgb(255, 204, 204);">uid={$uid}</span>";

两个查询语句都经过php的addslashes函数过滤转义,但是安全性能方面仍不相同:
第一条:变量被单引号保卫,要想注入需先闭合前面的单引号,并且注释掉原sql语句的后面的单引号。又因为使用了addslashes函数,结果可想而知。。。
第二条:可将字符串转换成16进制实现注入。

4、绑定变量,使用预编译语句
MySQL的 mysqli驱动提供了预编译语句的支持,不同的程序语言,都分别有使用预编译语句的方法,我们这里仍然以PHP为例,编写 userinfo2.php代码:

<?php
header('Content-type:text/html; charset=UTF-8');
$username = isset($_GET['username']) ? $_GET['username'] : '';
$userinfo = array();
if($username){
       //使用mysqli驱动连接demo数据库
        $mysqli = new mysqli("localhost", "root", "root", 'demo');
     //使用问号替代变量位置
 $sql = "SELECT uid,username FROM user WHERE username=?";
        $stmt = $mysqli->prepare($sql);
      //绑定变量
       $stmt->bind_param("s", $username);
     $stmt->execute();
        $stmt->bind_result($uid, $username);
 while ($stmt->fetch()) {
        $row = array();
        $row['uid'] = $uid;
       $row['username'] = $username;
     $userinfo[] = $row;
       }
}
echo '<pre>',print_r($userinfo, 1),'</pre>';

从上面的代码可以看到,我们程序里并没有使用addslashes函数,但是浏览器里运行 http://localhost/test/userinfo2.php?username=zheng' AND 1=1-- hack里得不到任何结果,说明SQL漏洞在这个程序里并不存在。


实际上,绑定变量使用预编译语句是预防SQL注入的最佳方式,使用预编译的SQL语句语义不会发生改变,在SQL语句中,变量用问号 ?表示,黑客即使本事再大,也无法改变SQL语句的结构,像上面例子中,username变量传递的 plhwin' AND 1=1-- hack参数,也只会当作username字符串来解释查询,从根本上杜绝了SQL注入攻击的发生。



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值