由一次SQL注入去理解防SQL注入

SQL注入是PHP運用最常見的漏洞之一,很多開發人員都會時刻提防着它,防SQL注入的普遍做法是對數據輸入進行過濾,以及對發送到數據庫的數據進行轉義。其實就是永遠不要相信用戶輸入數據。爲了更好的理解SQL注入,筆者今天自己嘗試用SQL“攻擊”自己了一次。以下是建立攻擊的一個過程。

1、建立用戶表
這裏寫圖片描述
我用PHPMyAdmin在我test數據庫建立了一張user表,表中有三個字段,分別是用戶名、密碼、郵箱,然後插入了一條測試數據
這裏寫圖片描述
用戶名是:周運金,密碼是test(用了MD5加密)
2、建立登陸頁面form.html

<html>
<head>
<title>Sql注入</title>
<meta http-equiv="content-type"content="text/html;charset=utf-8">
</head>
<body>
<form action="validate.php" method="post">
  <fieldset >
    <legend>Sql注入</legend>
    <table>
      <tr>
        <td>用戶名:</td>
        <td><input type="text" name="username"></td>
      </tr>
      <tr>
        <td>密  碼:</td>
        <td><input type="text" name="password"></td>
      </tr>
      <tr>
        <td><input type="submit" value="提交"></td>
        <td><input type="reset" value="重置"></td>
      </tr>
    </table>
  </fieldset>
</form>
</body>
</html>

效果:
這裏寫圖片描述
3、創建處理登陸Validate.php

<?php

      //面向對象的連接方式
     $mysqli =new mysqli("localhost","root","123","test");
      if(!$mysqli ){
               echo mysqli_connect_error();
           }
     $mysqli->set_charset("utf8");                    $mysqli->query("set names 'utf8'");
      $name=$_POST['username'];
      $pwd=md5($_POST['password']);
      $sql="select * from user where username='$name' and password='$pwd'";
      $query=$mysqli->query($sql);
      if($query == false){
            echo $mysqli->error;
      }
      else{
            $rows =$query->num_rows;
            if($rows){
                   header("Location:manage.php");
             }else{
                   echo "您的用戶名或密碼輸入有誤,<a href='form.html'>請重新登錄!</a>";
             }
      }
     $mysqli->close();
?>

這裏說明一下,原始MySQL在PHP5.5之後已經被php拋棄,採用面像對象連接的MYSQLI。
4、建立登陸成功的頁面manage.php

<?php 

echo "You are a manager";
 ?>

這樣就完成了一個有SQL注入漏洞的登陸程序了。很明顯程序沒有對用戶輸入的數據進行處理就直接放進sql語句裏面了。這是很危險的做法。
接下來就開始攻擊一下吧,做法其實很簡單隻要去拼湊sql語句就好了。
我先輸入正確的用戶名和密碼:
這裏寫圖片描述
爲了顯示密碼我這裏沒有用密碼框了。輸入完之後成功登陸
這裏寫圖片描述
接下來正式注入了,用戶名輸入:’ or 1=1# 密碼隨便輸入
這裏寫圖片描述
點擊提交之後:
這裏寫圖片描述
居然登陸成功了。哈哈哈,接下來分析一下注入後SQL語句吧:
我在sql語句那裏設置了一個斷點,看一下拼接後的sql
這裏寫圖片描述
這裏寫圖片描述
這裏可以看到拼接後的sql語句是:
select * from user where username=” or 1=1#’ and password=’202cb962ac59075b964b07152d234b70’這裏的#是mysql的註釋符,意思就是忽略後面的sql語句,這樣的話就不用驗證了,而且在username後面還有一句邏輯語句or 1=1,這樣的話這條語句永遠成立,所以就通過了驗證。

接下來就談談常見的方SQL注入方法:
1、最常見的就是採用mysqli_real_escape_string函數進行轉義一些特殊的字符比如\n、\r、\、’、” 等(在查找mysql_real_escape_string函數的時候發現PHP文檔說這個函數在php5.5之後就被拋棄了,改用mysqli_real_escape_string,看來PHP要全面使用面向對象的mysqli了),就像剛纔的注入有個單引號,用了這個函數之後就會被轉義,這樣拼接就失敗了。我們來看看再用之前的’ or 1=1#去拼接,加入這個函數之後會怎樣:

      $name=mysqli_real_escape_string($mysqli,$_POST['username']);      //必須使用數據庫連接,這樣看來是專門爲防sql注入準備的,比較安全
      $pwd=mysqli_real_escape_string($mysqli,md5($_POST['password']));

這裏寫圖片描述
結果不能登錄了,設個斷點看一下轉義之後sql語句:
這裏寫圖片描述
這個是PHP文檔給出的例子

<?php

     // Assume this is a simple comments form with a name and comment.

     $name = mysqli_real_escape_string($conn, $_POST['name']);
     $comments = mysqli_real_escape_string($conn, $_POST['comments']);

     // Here is where most of the action happens.  But see note below
     // on dumping back out from the database

     // We should use the ENT_QUOTES flag second parameter...
     $name = htmlspecialchars($name);
     $comments = htmlspecialchars($comments);

     $insert_sql = "INSERT INTO tbl_comments ( c_id, c_name, c_comments ) VALUES ( DEFAULT, '" . $name . "', '" . $comments . "')";

     $res = mysqli_query($conn, $insert_sql);
     if ( $res === false ) {
          // Something went wrong, handle it
     }

     // Now output page showing comments
?>

看到單引號‘被轉義成了/’了這樣的話拼接就沒用了。
二、打開magic_quotes_gpc來防止SQL注入
這個原理跟第一個的原理類似,是將GET、POST、COOKIE傳過來的數據進行自動轉義,相當於用addslshes()函數進行轉義。但是這種方式沒有辦法防止當參數是數字型的sql攻擊,因爲數字是沒有單引號或者雙引號的。解決的辦法是用intaval()函數強制將字符數據轉換成數字。如果開啓了magic_quotes_gpc=on,在第一個方法中記得用stripslashes函數去掉/
三、自定義過濾函數
以下是W3C給出的一個過濾函數我將轉義函數改了

function check_input($value,$con)
{
// 去除斜槓
if (get_magic_quotes_gpc())
  {
  $value = stripslashes($value);
  }
// 如果不是數字則加引號
if (!is_numeric($value))
  {
  $value = "'" . mysqli_real_escape_string($con,$value) . "'";
  }
return $value;
}

這個函數考慮到使用mysqli_real_escape_string比使用addslshes()更加安全

最後注意了:
在寫這篇文章的時候,筆者參考了一篇文章說sql注入可以繞開以上的方法了,嚇得得我一身冷汗,看了之後果然覺得厲害。文章中建議使用不要用mysql_query了而是用PDO和MYSQLi來代替mysql_query,心想還好我用的是mysqli,它採用了mysqli的Prepared Statement機制可以有效解決sql注入。大家可以參考一下
文章地址:參考文章

總結:
1、sql注入的方式其實很簡單,但是後果卻是很致命的,所以開發人員一定記住永遠不要去相信客戶的數據。
2、防sql注入的方法有很多,但是一定得保持一定得技術更新,因爲黑客的技術越來越厲害了,要經常更新這方面的防護知識,隨時保持最新的防漏洞知識。

發佈了28 篇原創文章 · 獲贊 7 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章