SQL注入原理及防範

    前段時間部門遇到SQL注入攻擊,在此,我也分享一下自己的經驗和理解。

    首先一個很重要的論點:SQL注入是可以完全杜絕的

SQL注入原因

    通俗點講,SQL注入的根本原因是: "用戶輸入數據"意外變成了代碼被執行。

    "用戶輸入數據"我這裏可以指Web前端$_POST,$_GET獲取的數據,也可以指從數據庫獲取的數據,當然也不排除程序猿無意中使用的特殊字符串。

    在SQL語句的拼接中,一些含特殊字符的變量在拼接時破壞了SQL語句的結構,導致"用戶輸入數據"意外變成了代碼被執行。

SQL語句拼接

    以PHP語言爲例,PHP中SQL拼接方式主要有以下幾種

1.  數值類型 直接拼接

例如
$sql = 'SELECT * from table where id = '. $id
                 $sql = "SELECT * from table where id = $id ";

這裏開發者默認將$id當做數值類型,進行直接拼接。調試時,當$id爲字符串時會報錯,所以可以提前發現拼接錯誤。通常會用intval($id),floatval($id)強制轉換成數值型,並不會造成SQL注入。

2.  字符串型拼接

例如
$sql = 'SELECT * from table where name = '."$name"
        $sql = "SELECT * from table where name = '$name' ";

這裏開發者默認將$name當做字符串處理。當$name中含有單引號(')或 雙引號("),會抵消掉$name變量上的單引號或雙引號,從而破壞了$sql的結構使得$name有可能變成SQL命令被執行,這也是爲什麼一些字符轉義函數如addslashes()可以防止簡單的SQL注入。

除了$name直接含有單/雙引號('/")外,由於一些特殊的編碼(如特定的漢子字符)也可以抵消$name變量上的單引號或雙引號,所以addslashes()、mysql_escape_string()、mysql_real_escape_string() 這種轉義特殊字符的方法是有風險的。

但無論編碼情況多麼複雜,SQL注入有一點是不變的:  抵消 單/雙引號,從而破壞SQL語句結構。理解這一點,可以指導開發者編碼,從而加強開發者對SQL注入風險的控制能力。

防止SQL注入方法

      在此我把方法歸納爲3類。

    1.字符(串)過濾法  (個人認爲是網上的一種僞方法,注入後減小損失,以自我安慰,其實已經注入了)

 通常過濾字符串變量中一些敏感詞和字符,如" '|and|exec|insert|select|delete|update|count|*|%|chr|mid|master|truncate|char|declare|;|or|-|+|, "等,或者用正則表達式過濾傳入的參數,類似的方法網上有較多參考。

       但是個人覺得字符(串)過濾法並不適合防止SQL注入,因爲字符(串)過濾對防止破壞SQL語句結構毫無用處(過濾單/雙引號除外),字符(串)過濾法更側重SQL語句結構破壞後,阻止侵入者執行他的代碼意圖。(而實際上自己的SQL語句結構已經被破壞了)。

   個人認爲這是一種僞方法,是把字符過濾和防止SQL注入兩個概念混淆。字符串過濾更是業務層面上的要求,過濾用戶輸入的'insert ,and,delete'等關鍵詞是否符合業務需是需要考慮的。 

    2.字符轉義法  (不推薦使用,無法避免編碼注入風險)

       addslashes()    (不推薦使用,只能防止 單/雙 引號注入)

         addslashes()是在 單引號(')、雙引號(")、反斜線(\)與 NUL(NULL 字符)前加上了反斜線,但是遇到一些特殊編碼也無能爲力。如:含0xbf27 的字就不會,所以 select * from table where name='縗' OR 1 就會產生SQL注入。在上面的SQL拼接中 $sql = " SELECT * from table where name = '$name' "中,如果$name = "'縗' or 1", 用$name = addslashes($name) 還是會產生SQL注入.

    mysql_escape_string()、mysql_real_escape_string()   (不推薦使用,無法避免編碼注入風險,且只適用於MySQL)

        mysql_escape_string 和 mysql_real_escape_string功能一樣,都是在以下字符前添加反斜槓: \x00, \n, \r, \, ', " 和 \x1a。mysql_real_escape_string使用之前要先連接上數據庫,會考慮當前鏈接connection字符集。

      字符轉義法並不能完全避免SQL注入風險,應謹慎使用。

      由於一些複雜的業務邏輯,無法完全避免SQL語句拼接,在變量可控的情況下,使用這種字符轉義法也未嘗不可,比不使用直接拼接要強得多。

    3.預處理語句法  (解析協議層面上完全規避SQL注入)

         PDO

$pdo = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'pass');  
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);  
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);  
$stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');  
$stmt->execute(array('name' => $name));  
foreach ($stmt as $row) {
}

         MYSQLi

$stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
$stmt->bind_param('s', $name);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
}

      預處理語句方法會先解析SQL語句,然後通過傳入不同的參數值來執行SQL,不需要每次執行都解析SQL語句,查詢執行路徑比常規查詢短,所以預處理語句方法效率更高;預處理語句在SQL語句解析協議上避免將參數當做SQL命令執行,僅僅當做值傳遞,所以可以完全避免SQL注入。

    總之,預處理語句方法可以完全避免SQL注入,並且效率更高。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章