假設我們的用戶表中存在一行.用戶名字段為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' ?>
可以看到.pd內部的注入已經被轉義.當成一個完整的字符串了..這樣的話.就不可能被注入了.
轉於http://blog.csdn.net/wusuopubupt/article/details/9668501
