リテラシーステッカー-ユニバーサルパスワードの原則

//连接数据库
  $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_row関数が返されたデータの数を判断することがわかります。返された数が1より大きい場合ユーザーがデータベースに存在し、ログインが許可されていることを証明します。通常の状況で実行されるSQLステートメントは次のとおりです。

[AppleScript] プレーンテキストビューの コピーコード

1

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


ただし、['または' 1 '=' 1]を送信すると、プログラムは特殊文字をフィルタリングしないため、ユーザー入力が直接クエリに取り込まれ、最後に実行されたSQLステートメントは次のようになります。

[AppleScript] プレーンテキストビューの コピーコード

1

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


このように、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