mysql注入攻擊及防範

1、注入攻擊種類
     1. GET注入
         輸入參數經過URL發送。
     2. POST注入
         輸入參數經過HTTP正文發送
     3. COOKIE注入
         輸入參數經過HTTP cookie發送
     4. HTTP Headers注入
         經過http提交應用程序時使用的頭
 
2、各類攻擊所佔比及防範程度
    1. 漏洞發現佔比
        
        從上圖中,能夠看到,大部分的http header和http cookie漏洞不能被發現。
    2. 漏洞防範佔比
        
        從上圖中,能夠得出,大部分的攻擊防範上,在於get和post,而對於cookie和header則不多,所以很容易在這方面失守。
 
3、注入攻擊實驗分析
     1. 建立實驗數據庫,分兩種類型編碼:GBK/UTF8
$createSqlGbk = "CREATE TABLE `user` (`id`  int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '用戶ID' ,`username`  varchar(32) NOT NULL COMMENT '用戶名' ,`password`  varchar(32) NOT NULL ,`remark`  text NULL ,PRIMARY KEY (`id`)) DEFAULT CHARACTER SET=gbk";
$createSqlUtf8 = "CREATE TABLE `user` (`id`  int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '用戶ID' ,`username`  varchar(32) NOT NULL COMMENT '用戶名' ,`password`  varchar(32) NOT NULL ,`remark`  text NULL ,PRIMARY KEY (`id`)) DEFAULT CHARACTER SET=utf8";
        其中對於多字節編碼的數據庫,數據在PHP層轉義時,GBK存在BUG,所以能夠進行測試對比。
     2. 初始化數據庫
$insertDataSql = "INSERT INTO `user` VALUES ('1', 'test', '123456', null), ('2', 'guest', '888888', null), ('3', 'admin', '888888', null)";
        其中構建一個簡單表,放幾行簡單的數據,用於測試。
     3. 構建測試代碼

     
     
     
     
< html>
    < head>
        < title>SQL注入攻擊與防範</ title>
        < meta http-equiv= "Content-Type" content= "text/html; charset=UTF-8"/>
        < style>
             body{ font- size: 12px;margin: 20px;}
             h3{ font- size: 14px;margin: 20px 0;}
             a{ font- size: 12px;margin: 10px 20px;}
        </ style>
    </ head>
    < body>
        < h3>SQL注入攻擊(GBK)</ h3>
        < a href= "./inject.php?type=gbk&id=1">正常取值</ a>
        < a href= "./inject.php?type=gbk&id=2341 or 1 = 1 -- dasf">SQL注入(針對直接等於寫法)</ a>
        < a href= "./inject.php?type=gbk&id=2341' or 1 = 1 -- asdf">SQL注入(針對用引號包括寫法)</ a>
        < a href= "./inject.php?type=gbk&isslashes=1&id=2341<?php echo chr(0xbf) . chr(0x27); ?> or 1 = 1 -- asd">SQL注入(針對對特殊字符轉義寫法)</ a>
        < h3>SQL注入攻擊(UTF8)</ h3>
        < a href= "./inject.php?type=utf8&id=1">正常取值</ a>
        < a href= "./inject.php?type=utf8&id=2341 or 1 = 1 -- dasf">SQL注入(針對直接等於寫法)</ a>
        < a href= "./inject.php?type=utf8&id=2341' or 1 = 1 -- asdf">SQL注入(針對用引號包括寫法)</ a>
        < a href= "./inject.php?type=utf8&isslashes=1&id=2341<?php echo chr(0xbf) . chr(0x27); ?> or 1 = 1 -- asd">SQL注入(針對對特殊字符轉義寫法)</ a>
        < div style= "width:100%;height:5px;margin:10px 0px;clear:both;background-color:gray;"></ div>
        <?php
        if (isset($_GET[' type'])) {
            $ type = $_GET[' type'];
        } else {
            exit();
        }
        echo "<h1 style='font-size:12px;'>CHARSET:: <b style='color:red;font-size:18px;'>" . $ type . "</b></h1>";
        // 創建mysql鏈接
        $c = mysql_connect( "localhost", "root", "");
        echo "<h1 style='font-size:14px;color:red;'>SQL注入攻擊及防範</h1>";
 
        if (isset($_GET[' id'])) {
            if (isset($_GET['isslashes'])) {
                $ id = addslashes($_GET[' id']);
            } else {
                $ id = $_GET[' id'];
            }
            // 三種sql寫法
            $sql1 = "SELECT * FROM user WHERE id = $id";
            $sql2 = "SELECT * FROM user WHERE id = '{$id}'";
            $sql = "SELECT * FROM user WHERE id = " . intval($ id);
        }
        //根據編碼不一樣,選擇鏈接對應編碼數據庫
        if ($ type == 'gbk') {
            mysql_select_db( "testInjectionDb_gbk", $c);
            // change our character set
            mysql_query( "SET CHARACTER SET 'gbk'", $c);
        } else {
            mysql_select_db( "testInjectionDb_utf8", $c);
            // change our character set
            mysql_query( "SET CHARACTER SET 'utf8'", $c);
        }
        // 依次執行三種寫法的查詢
        echo "<div style='font-size:12px;'>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++</div>";
        echo "<h1 style='font-size:12px;'>被攻擊 SQL1::   <b style='color:red;font-size:14px;'>" . $sql1 . "</b></h1>";
        $r1 = mysql_query($sql1, $c);
        if ($r1) {
            echo "<h1 style='font-size:12px;color:green;'>影響數據條數:: <b style='color:red;font-size:18px;'>" . mysql_num_rows($r1) . "</b></h1>";
        } else {
            echo "<h1 style='font-size:12px;color:red;'>語法錯誤!</h1>";
        }
        echo "<div style='font-size:12px;'>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++</div>";
        echo "<h1 style='font-size:12px;'>被攻擊 SQL2::   <b style='color:red;font-size:14px;'>" . $sql2 . "</b></h1>";
        $r2 = mysql_query($sql2, $c);
        if ($r2) {
            echo "<h1 style='font-size:12px;color:green;'>影響數據條數:: <b style='color:red;font-size:18px;'>" . mysql_num_rows($r2) . "</b></h1>";
        } else {
            echo "<h1 style='font-size:12px;color:red;'>語法錯誤!</h1>";
        }
        echo "<div style='font-size:12px;'>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++</div>";
        echo "<h1 style='font-size:12px;'>強制整型較驗 SQL::   <b style='color:red;font-size:14px;'>" . $sql . "</b></h1>";
        $r = mysql_query($sql, $c);
        if ($r) {
            echo "<h1 style='font-size:12px;color:green;'>影響數據條數:: <b style='color:red;font-size:18px;'>" . mysql_num_rows($r) . "</b></h1>";
        } else {
            echo "<h1 style='font-size:12px;color:red;'>語法錯誤!</h1>";
        }
 
        // 創建PDO的鏈接,萬能防注入。
        // 注意:
        // 1. 參數 PDO::ATTR_EMULATE_PREPARES 設置爲 false;
        // 2. 你不能讓佔位符 ? 代替一組值,如: SELECT * FROM blog WHERE userid IN ( ? );
        // 3. 你不能讓佔位符代替數據表名或列名,如: SELECT * FROM blog ORDER BY ?;
        // 4. 你不能讓佔位符 ? 代替任何其餘SQL語法,如: SELECT EXTRACT( ? FROM datetime_column) AS variable_datetime_element FROM blog;
        echo "<div style='font-size:12px;'>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++</div>";
        if ($ type == 'gbk') {
            $pdo = new PDO( "mysql:host=localhost;dbname=testInjectionDb_gbk", "root", "");
        } else {
            $pdo = new PDO( "mysql:host=localhost;dbname=testInjectionDb_utf8", "root", "");
        }
        $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, FALSE);
        $st = $pdo->prepare( "SELECT * FROM user WHERE id = ?");
        $st->bindParam( 1, $ id);
        $c = $st->execute();
        echo "<h1 style='font-size:12px;'>PDO防注入(萬能防注入) SQL::   <b style='color:red;font-size:14px;'>" . $st->queryString . "</b></h1>";
        if ($c) {
            $rs = $st->fetchAll();
            echo "<h1 style='font-size:12px;color:green;'>影響數據條數:: <b style='color:red;font-size:18px;'>" . count($rs) . "</b></h1>";
            var_dump($rs);
            $st->debugDumpParams();
        } else {
            echo "<h1 style='font-size:12px;color:red;'>語法錯誤!</h1>";
        }
        ?>
    </ body>
</ html>
        以上代碼構建了兩種編碼(gbk/utf8),執行邏輯分四種實現方式,各四種訪問方式,其中第一種爲正常訪問,後三種分別爲注入型寫法。
        
    4. 四種實現寫法
$sql1 = "SELECT * FROM user WHERE id = $id";$sql2 = "SELECT * FROM user WHERE id = '{$id}'";$sql = "SELECT * FROM user WHERE id = " . intval($id);
$st = $pdo->prepare("SELECT * FROM user WHERE id = ?");$st->bindParam(1, $id);$c = $st->execute();
    5. 應對攻擊的表現
<a href="./inject.php?type=gbk&id=2341 or 1 = 1 -- dasf">SQL注入(針對直接等於寫法)</a>
<a href="./inject.php?type=utf8&id=2341 or 1 = 1 -- dasf">SQL注入(針對直接等於寫法)</a>
    
    
    
    
        從上面能夠很明確的看到,在所傳參數中,帶有注入性語句。表現結果:
         GBK編碼:
        
        UTF8編碼:
        
        從中能夠看到,兩種編碼的數據庫中,對於 SQL1 的寫法是實現了成功注入。
 
<a href="./inject.php?type=gbk&id=2341' or 1 = 1 -- asdf">SQL注入(針對用引號包括寫法)</a>
<a href="./inject.php?type=utf8&id=2341' or 1 = 1 -- asdf">SQL注入(針對用引號包括寫法)</a> 
      
        從測試的結果中,能夠知道,對於兩種編碼,都實現了對 SQL2 寫法的注入攻擊。
<a href="./inject.php?type=gbk&isslashes=1&id=2341<?php echo chr(0xbf) . chr(0x27); ?> or 1 = 1 -- asd">SQL注入(針對對特殊字符轉義寫法)</a>
<a href="./inject.php?type=utf8&isslashes=1&id=2341<?php echo chr(0xbf) . chr(0x27); ?> or 1 = 1 -- asd">SQL注入(針對對特殊字符轉義寫法)</a>
        其中在連接中輸出 chr(0xbf) . chr(0x27) ,主要是爲了測試多字節編碼的值,通過轉義後,對於GBK存在BUG,addslashes處理後,前一個字符會被轉義爲一個多字節的合法字符,然後面的 chr(0x27) 則自動成了一個單引號,從而實現字符串截斷,形成注入。而相對於GBK,UTF8編碼的數據庫,在編碼轉義過程當中,不存在該問題。
         GBK表現(注入成功)
        
        UTF8表現
        
        對於第三種寫法,由於 ID 在查詢過程當中,明確是整型數值,所以,在查詢前,強制進行整型校驗,因此在這幾種攻擊方式中,都沒能實現注入,所以,對於此種類型的數據查詢條件,爲了安全,咱們能夠進行相應的校驗,從而保證安全,但對於其餘類型的變量,則不太好處理。
         綜合上面的幾種表現,最好的方式是以 PDO 方式來實現數據庫的查詢邏輯,只需注意如下幾點便可:
        1. 參數 PDO::ATTR_EMULATE_PREPARES 設置爲 false;
        2. 你不能讓佔位符 ? 代替一組值,如:SELECT * FROM blog WHERE userid IN ( ? );
        3. 你不能讓佔位符代替數據表名或列名,如:SELECT * FROM blog ORDER BY ?;
        4. 你不能讓佔位符 ? 代替任何其餘SQL語法,如:SELECT EXTRACT( ? FROM datetime_column) AS variable_datetime_element FROM blog;
 
4、總結
     sql注入,能夠從多方面來進行防範,在可能的狀況下,若是數據庫須要存儲多字節字符,建議數據庫的編碼方式儘可能採用UTF8;另外,在查詢數據庫的邏輯實現過程當中,咱們要增強對於語句編寫方面的一些安全意識,如,對於整型數值進行強制性的類型校驗等;在解決方案上,建議採用 PDO 方式來實現相關的數據庫操做邏輯。
     而對於另外幾種攻擊方式,區別在於傳送攻擊的方式不一樣,原理仍是同樣,因此再也不贅敘。
 
附件:
 

附件列表

相關文章
相關標籤/搜索