sql參數綁定防止注入的原理

本文主要講述爲什麼拼接sql容易sql注入 , 而sql使用參數綁定可以防止sql 注入
假設我們的用戶表中存在一行.用戶名字段爲username.值爲aaa.密碼字段爲pwd.值爲pwd…
下面我們來模擬一個用戶登錄的過程…

<?php    
$username = "aaa";    
$pwd = "pwd";    
$sql = "SELECT * FROM table WHERE username = '{$username}' AND pwd = '{$pwd}'";    
echo $sql; //輸出  SELECT * FROM table WHERE username = 'aaa' AND pwd = 'pwd'    
?>   

這樣去執行這個sql語句.顯然是可以查詢出來東西的.返回用戶的這一列.登錄成功!!
然後我改一下…把密碼改一下.隨便一個值.如下.我改成了ppp.

<?php    
$pwd = 'ppp';    
$sql = "SELECT * FROM table WHERE username = '{$username}' AND pwd = '{$pwd}'";    
echo $sql; //輸出  SELECT * FROM table WHERE username = 'aaa' AND pwd = 'ppp'    
?>  

這樣很顯然.如果去執行這個SQL語句…是查詢不到東西的.也就是密碼錯誤.登錄失敗!!
但是有的人總是不老實的.他們會想盡一切辦法來進行非法的登錄.所謂非法就是在他不知道用戶名密碼的時候進行登錄.並且登錄成功…

那麼他們所做的原理是什麼呢??其實原理都是利用SQL語句…SQL語句強大的同時也給我們帶來了不少麻煩…
我來舉個最簡單的例子.我們要運用到的SQL關鍵字是or
還是上面的代碼.我們只要修改一下密碼即可

<?php    
$username = "aaa";    
$pwd = "fdsafda' or '1'='1";  //前面的密碼是瞎填的..後來用or關鍵字..意思就是無所謂密碼什麼都執行    
$sql = "SELECT * FROM table WHERE username = '{$username}' AND pwd = '{$pwd}'";    
echo $sql;  //輸出  SELECT * FROM table WHERE username = 'aaa' AND pwd = 'fdsafda' or '1'='1'    
?>   

執行一下這個SQL語句…可怕的事情發生了…竟然可以查詢到這一行數據…也就是登錄成功了…
這是多麼可怕的事情…

SQL注入演示教程,見博文:http://blog.csdn.net/wusuopubupt/article/details/8818996

PHP爲了解決這個問題.magic_quotes state…就是PHP會自動過濾傳過來的GET.POST等等.
題外話.實踐證明這個東西是畸形的…大部分程序不得不爲判斷此功能而耗費了很多代碼…
在Java中可沒有這個東西…那麼Java中如何防止這種SQL注入呢??

Java的sql包中提供了一個名字叫PreparedStatement的類.
這個類就是我要說的綁定參數!
什麼叫綁定參數??我繼續給大家舉例…(我用PHP舉例)

<?php    
$username = "aaa";    
$pwd = "pwd";    
$sql = "SELECT * FROM table WHERE username = ? AND pwd = ?";    
bindParam($sql, 1, $username, 'STRING');  //以字符串的形式.在第一個問號的地方綁定$username這個變量    
bindParam($sql, 2, $pwd, 'STRING');       //以字符串的形式.在第二個問號的地方綁定$pwd這個變量    
echo $sql;    
?>  

當然.到此.你肯定不知道會輸出什麼…更無法知道綁定參數有什麼好處!這樣做的優勢是什麼.更不知道bindParam這個函數到底做了什麼.
下面我簡單的寫一下這個函數:

<?php    
/**   
 * 模擬簡單的綁定參數過程   
 *   
 * @param string $sql    SQL語句   
 * @param int $location  問號位置   
 * @param mixed $var     替換的變量   
 * @param string $type   替換的類型   
 */   
$times = 0;    
//這裏要注意,因爲要“真正的"改變$sql的值,所以用引用傳值  
function bindParam(&$sql, $location, $var, $type) {    
    global $times;    
    //確定類型    
    switch ($type) {    
        //字符串    
        default:                    //默認使用字符串類型    
        case 'STRING' :    
            $var = addslashes($var);  //轉義    
            $var = "'".$var."'";      //加上單引號.SQL語句中字符串插入必須加單引號    
            break;    
        case 'INTEGER' :    
        case 'INT' :    
            $var = (int)$var;         //強制轉換成int    
        //還可以增加更多類型..    
    }    
    //尋找問號的位置    
    for ($i=1, $pos = 0; $i<= $location; $i++) {    
        $pos = strpos($sql, '?', $pos+1);    
    }    
    //替換問號    
    $sql = substr($sql, 0, $pos) . $var . substr($sql, $pos + 1);   
}    
?>   

注:由於得知道去除問號的次數…所以我用了一個global來解決.如果放到類中就非常容易了.弄個私有屬性既可

通過上面的這個函數.我們知道了…綁定參數的防注入方式其實也是通過轉義進行的…只不過是對於變量而言的…
我們來做一個實驗:

<?php    
$times = 0;    
$username = "aaaa";    
$pwd = "123";    
$sql = "SELECT * FROM table WHERE username = ? AND pwd = ?";    
bindParam($sql, 1, $username, 'STRING');  //以字符串的形式.在第一個問號的地方綁定$username這個變量    
bindParam($sql, 2, $pwd, 'INT');       //以字符串的形式.在第二個問號的地方綁定$pwd這個變量    
echo $sql;  //輸出  SELECT * FROM table WHERE username = 'aaaa' AND pwd = 123    
?>   

可以看到.生成了非常正規的SQL語句.那麼好.我們現在來試下剛纔被注入的那種情況

<?php    
$times = 0;    
$username = "aaa";    
$pwd = "fdsafda' or '1'='1";    
$sql = "SELECT * FROM table WHERE username = ? AND pwd = ?";    
bindParam($sql, 1, $username, 'STRING');  //以字符串的形式.在第一個問號的地方綁定$username這個變量    
bindParam($sql, 2, $pwd, 'STRING');       //以字符串的形式.在第二個問號的地方綁定$pwd這個變量    
echo $sql; //輸出  SELECT * FROM table WHERE username = 'aaa' AND pwd = 'fdsafda\' or \'1\'=\'1'    
?>   

可以看到.pwd內部的注入已經被轉義.當成一個完整的字符串了…這樣的話.就不可能被注入了.

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