扫盲贴-万能密码的原理

//连接数据库
  $coon = mysql_connect('localhost','root','root') or die('Connect Error '.mysql_error());
  mysql_select_db('sqlinjection',$coon) or die('Datebase Error '.mysql_error());
//构造查询语句
  $query="SELECT * FROM user WHERE username='".$_GET["username"]."' AND password='".$_GET["password"]."'";
  echo $query."<br/>";
//执行sql语句
  $result= mysql_query($query);
//echo $result;
//判断返回数据的数目
  $rowcount=mysql_num_rows($result);
//判断 如果数量大于1标示登录成功
//echo $rowcount;
  if($rowcount!=0){
    echo "OK";
  }else{
    echo "ERROR";
  }
 ?>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>SQL injection test</title>
  </head>
  <body>
    <form class="" action="test.php" method="GET">
      <input type="test" name="username" value=""><br/>
      <input type="password" name="password" value=""><br/>
      <input type="submit" name="sub" value="Login">
    </form>
  </body>
</html>

首先我们先看一下测试代码,首先可以看到的是参数并没有进行相关的过滤,肯定会产生注入,但是今天我们讨论的是万能密码的问题。
     关于万能密码我们应该都知道,常见的万能密码包括:

1:"or "a"="a
2: ')or('a'='a
3:or 1=1--
4:'or 1=1--
5:a'or' 1=1--
6:"or 1=1--
7:'or'a'='a
8:"or"="a'='a
9:'or''='
10:'or'='or'
11:1 or '1'='1'=1
12:1 or '1'='1' or 1=1
13: 'OR 1=1%00

通过我们的测试代码可以发现,开发在验证登录的时候,只是将用户输入作为查询条件进行查询,然后mysql_num_rows函数进行判断返回数据的数量,如果返回的数量大于1证明数据库中存在该用户,然后允许登录,正常情况下执行的SQL语句为:

[AppleScript] 纯文本查看 复制代码

?

1

select * from user where username='admin' and password ='admin'


但是当我们提交 【' or '1'='1】时,由于程序并没有对特殊字符进行过滤,将用户输入的直接带入查询,最后执行的SQL语句为:

[AppleScript] 纯文本查看 复制代码

?

1

select * from user where username='admin' and password ='admin' or '1'='1'

扫描二维码关注公众号,回复: 13060400 查看本文章


这样SQL返回的值始终为1,就成功绕过了程序的验证。

关于修复:
 

[PHP] 纯文本查看 复制代码

?

1

2

3

4

5

6

7

8

9

$result=mysql_query($sql); //执行语句

  //echo $result;

  $results=mysql_fetch_assoc($result);

  if($results['username'] == $name && $results['password'] == $pass){

    echo "OK";

  }else{

    echo "NO";

  }

  }



以上代码对返回的数据与用户输入数据进行了比较验证,避免了万能密码的产生。

参数化查询如何解决掉了SQL注入

先看一段存在sql注入的代码:

[PHP] 纯文本查看 复制代码

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

<?php

  //数据库连接

  $mysqli=new mysqli('localhost','root','root','test');

  if ($mysqli->connect_errno) {

    die('ERROR:'.$mysqli->connect_errer);

  }

  //数据库查询操作

  if (!empty($_POST['sub'])) {

    //获取参数

    $user=$_POST['username'];

    $pass=md5($_POST['password']);

    //构造语句

    $sql="SELECT * FROM mysqli_test WHERE username='{$user}' AND password='{$pass}'";

    echo $sql;

    //执行语句

    $result=$mysqli->query($sql);

    //验证登录

    if($result && $result->num_rows>0){

      echo "<br /> OK";

    }else {

      echo "NO";

    }

  }

 ?>

<!DOCTYPE html>

<html>

  <head>

    <meta charset="utf-8">

    <title>Login </title>

  </head>

  <body>

    <form action="prepare.php" method="post">

      username:<input type="text" name="username" value=""><br/>

      password:<input type="password" name="password" value=""><br/>

      <input type="submit" name="sub" value="Login">

    </form>

  </body>

</html>


参数没有进行任何过滤 而且查询后没有对数据进行验证,看到这我们应该想到万能密码(http://bbs.ichunqiu.com/thread-10851-1-1.html
正常的查询如图

我们用万能密码尝试登录,我们可以看到红线之后的所有数据已经被注释掉,只有之前的语句被mysql所执行,同时后边的and 1=1返回永远为1,从而造成万能密码

下面看一下我们修改为预置参数查询后的代码:

[PHP] 纯文本查看 复制代码

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

<?php

  //数据库连接

  $mysqli=new mysqli('localhost','root','root','test');

  if ($mysqli->connect_errno) {

    die('ERROR:'.$mysqli->connect_errer);

  }

  //---------------------------------------参数化查询

  if (!empty($_POST['sub'])) {

      //获取参数

      $user=$_POST['username'];

      $pass=md5($_POST['password']);

      //构造语句

      $sql="SELECT * FROM mysqli_test WHERE username=? AND password=?";

      //准备执行sql语句

      $mysqli_stmt=$mysqli->prepare($sql);

      //传递参数

      $mysqli_stmt->bind_param('ss',$user,$pass);

      //执行语句 验证登录

      if ($mysqli_stmt->execute()) {

        //获取结果集 返回行数

        $mysqli_stmt->store_result();

        //验证登录

        if ($mysqli_stmt->num_rows>0) {

          echo "OK";

        }else {

          echo "NO";

        }

      }else {

          //  打印错误

          echo $mysqli_stmt->error;

      }

  }

 ?>

<!DOCTYPE html>

<html>

  <head>

    <meta charset="utf-8">

    <title>Login </title>

  </head>

  <body>

    <form action="prepare.php" method="post">

      username:<input type="text" name="username" value=""><br/>

      password:<input type="password" name="password" value=""><br/>

      <input type="submit" name="sub" value="Login">

    </form>

  </body>

</html>


我们正常登录  

我们看一下执行的语句(mysql日志)

我们再试一下万能密码  发现登录并没有成功

我们再看一下执行的语句

我们将将16进制转换一下


--------
我们将参数化查询与普通的查询对比一下

可以看到 参数化查询在真正执行之前会进行数据库预编译 放在缓存里面,当运行时动态地参数传递,即使参数里有敏感字符 数据库也会作为一个参数一个字段的属性值来处理而不会作为一个SQL指令。

猜你喜欢

转载自blog.csdn.net/qq_43422918/article/details/114981606