我很納悶…怎麼一堆 PHP 程式設計師不知道要怎麼防 SQL Injection
從 5 月份到 6 月份這段期間面試了好多個 PHP 程式設計師 , 包括也曾開職缺面試資訊主管
我都會問一個問題 , SQL Injection 怎麼防 ?
我說真的 , 大概面試了 10 來位 , 沒有一個人能夠脫口而出 , 不知道 SQL Injection 的程式設計師不會就算了 , 知道的人居然沒有一個人知道 PHP 原本就有功能可以過濾掉特殊字元 , 甚至還會回答用 str_replace 方法來過濾 , 所以我很想給看到這篇的人知道
如果你是用 mysql function , 那就是用 mysql_real_escape_string 去過濾
如果你是用 PDO , 就是用 bind value 作法
例如
1 2 3 4 5 | $sql = "SELECT * FROM users WHERE user_id=?"; $sth = $pdo->prepare($sql); $sth->execute( array($user_id) ); |
其實我是已經受夠了 !! 不知道是台灣的書籍太爛 , 還是學校裡面老師教的太爛 , 這種最簡單的防止方式居然都沒人知道 ?
舉個 PHP 原文上的例子
1 2 3 4 5 6 7 8 9 10 11 12 | <?php // Query database to check if there are any matching users $query = "SELECT * FROM users WHERE user='{$_POST['username']}' AND password='{$_POST['password']}'"; mysql_query($query); // We didn't check $_POST['password'], it could be anything the user wanted! For example: $_POST['username'] = 'aidan'; $_POST['password'] = "' OR ''='"; // This means the query sent to MySQL would be: echo $query; ?> |
上面的程式 , 會輸出
1 | SELECT * FROM users WHERE user='aidan' AND password='' OR ''='' |
那麼根本不用密碼就可以登入了 , 非常簡單的攻擊方式但卻又不得不重視 , 其實這種文字填空遊戲就是 SQL Injection ,
如果狠一點 , 還可以 DROP TABLE !!
瞭解了,個人理解:最終的目地就是要把SQL指令中變數所帶入的內容完全變成字串(包含特殊符號)
或者強制禁止只用任何特殊字元,至於方法就看個人經驗和功力了(要假設相當多的可能),
保險起見可以多做幾道,甚至不排除過濾掉高風險的指令比如DROP(看情況拉)
@shiang
我怎麼覺得你說的很複雜
不論任何來源的字串只要有被 sql driver 專用的指令轉換過的字串就不會被組合另一種 SQL 命令
mysql_real_escape_string
sql_escape_string
pg_escape_string
PDO::quote
…..
其實就算有bindValue()
有時候還是有些東西不好搞
比方這2個傢伙 %,_
關於Like的運算符是無法處理的~"~
所以在某些應用中為了閉開這個問題..不得加個自定函數
public function escape_like_string($str)
{
while(strpos($str,’%')!==false or strpos($str,’_')!==false)
{
$str=str_replace(array(‘%’,'_’),",$str);
}
return $str;
}
雖然目前這樣處理,可是我總覺得事情不是這麼一回事..可能有些概念上沒轉過去吧?
@Rie
確實 % 和 _ 在 bindValue 無法處理
如果有一段 sql 是
SELECT * FROM `users` WHERE `login` LIKE :keyword
$keyword = ‘%’ . $_GET['keyword'] . ‘%’;
如果不加以處理 , 萬一 $_GET['keyword'] 只輸入一個 %
那麼整段 SQL 將會變成
SELECT * FROM `users` WHERE `login` LIKE ‘%%%’
那會變成所有資料都 SELECT 出來了 , 而非去搜尋字串中有 % 的資料
所以這個確實要另外處理 , 不過通常會用到 like 主要是要去搜尋 ,
這問題只會影響搜尋結果 , 一般人也不太注意這個就是
但若資料很多的時候 , 被人家這麼搞法會把 CPU 操爆了 , 所以還是要去注意這個問題
除非有要 DELETE FROM tablexxx WHERE columnxxx LIKE …. 或 Update
真的有人會寫這樣的程式放在 Web 嗎 ??
@pigo
前後都萬用符其實比較少見,因為大家知道前面放%的話mysql會無視索引遍歷資料表
通常是用在select上沒錯
比較忌諱的結果是這種型態
$keyword = $_GET['keyword'] . ‘%’;
SELECT * FROM `users` WHERE `login` LIKE :keyword
同樣輸入%,結果雖然一樣,但這問題的真正意義在於,
這樣設計的原本目的是希望能夠在使用keyword時,還能利用索引
不得已還是得防阿 ~"~
@Rie
資料量小用 LIKE 還 ok
資料量大也沒有人用 LIKE 了 , 會不會用到索引都很慢 , 可以去試試 mobile01 的會員專用搜尋喔 , 設多個字串 會操掛
有一些對 MySQL 的全文索引 plugin 應該是比較好的解決之道
例如
bigram : http://www.pigo.idv.tw/archives/353 , http://www.pigo.idv.tw/archives/340
或
Sphinx 現在也有即時索引了 , Sphinx 我還沒空去好好把玩 , 很多深入的玩家都推薦這個的
$sql = "SELECT * FROM users WHERE user_id=?";
$sth = $pdo->prepare($sql);
$sth->execute( array($user_id) );
大大你好 我是在查詢PDO sql injection 防範時看到您的文章
我已經爬過文大概了解PDO連線方式
可是 我還是不知道為什麼 使用prepare()進行資料庫串列產生
在使用execute進行資料綁定可以預防 sql injection
因為我已經看過很多次 跟GOOGLE很多次
還是沒有很了解 所以想說留言詢問
是在做prepare()
還是execute( )
哪一個方法 可以預防sql injection
我本身也很重視資訊安全 最近要開發一個專案所以努力在尋求這答案~
感謝大大看完我留言~
@CHENG
PDO 有兩個物件
PDO 及 PDOStatment
PDO 本身就可以執行 sql 了 , 就是 PDO::query()
那就和原本的 mysql_query 一樣
因此先回想 mysql_query() 怎麼使用
mysql_query( "select * from users WHERE user=’" . $_POST['user'] . "‘");
如果單純這樣寫 , 被攻擊是一定會的
但如果
mysql_query("select * from users WHERE user=’" .mysql_real_escape_string($_POST['user']) . "‘");
就可以防止
同理 , PDO::query() 可以搭配 PDO::quote() 等同上述效果
至於為何有另一種 PDOStatment 物件的做法
我個人已經不曉得到底這種做法是從那一種語言開始看到 , java jdbc 很早前就有用這種做法 , 早期的 PHP ADODB 套件就有導入這種做法
好處就是你可以不用寫那麼多程式碼 , 至少可以不用下太多次 PDO::quote , 可以用陣列帶值進去取代就和 PDO::quote() 的效果可預防 sql injection 了
另外 PDOStatment 也讓程式有比較方便或彈性的設計 , 你可能要仔細摸才能品味到
經過看了6次您的回文 我稍微寫一下我的想法
像您說的 已經被學校或者外面書本教育到使用傳統連線已經習慣~
而且效率相當低 所以特別來找尋PDO相關的連線方式~
所以我稍微的去GOOGLE 找斷一段語法
// 組合 SQL 語法,取得符合 id = 2、name = ‘John’ 的資料
$sth = $dbh->prepare(‘SELECT * FROM table WHERE id = :id AND name = :name’);
$where = array(‘:id’ => 2, ‘:name’ => ‘John’);
// 使用 execute(),會自動 quote $where 的參數
$sth->execute($where);
所以簡單講~假設使用 prepare() 是做為宣告此資料庫連線,
然後使用 execute()做綁定動作時 execute()會自動quote傳入的變數達到簡短程式碼增加效率,
因為小弟目前是剛畢業的PHP 程式設計師,
每天用最多的 就是資料庫連線, 所以最近把PDO物件導向化,
希望把資料庫連線簡短且有效率,而不是寫得又臭又長又難維護,
查到 SQL Injection才來詢問您,還希望大大有機會可以認識認識,
因為對於台灣的資訊安全教育,我也真的很擔心所以目前在瘋狂的自我補強。
且你上面提到的 PDOStatment 也讓程式有比較方便或彈性的設計
其實我看到那段語法就想說 假設資料庫連線的宣告都先做成方法
例如 要使用 User Table時就Query(In User,$sql);
$sql=array(‘:id’ => 2, ‘:name’ => ‘John’);
把$sql帶入In User Table 這樣去就可以在下次使用時隨意更換資料庫
我這想法是不是就是你說的PDOStatment方便或彈性的設計嗎?
@CHENG
是啊 , 有很多種用法 , 看人怎麼設計
最簡單的做法 , 一個基礎的 db class , 裡面有個 $this->db , 然後其他的資料操作邏輯都可以寫成物件去繼承 db class , 就很好用了
恩 我目前使用的方式就是您說的
物件化 資料庫連線物件
之後再使用那問件去執行其他方法 只是最近在想該怎樣做可以做到防sql injection
且效能高 又不會很難維護的方法~ 目前已經有個大方向了~
HI~我又來了 大大我遇到一個我無解的問題 想說可以詢問你看看~
function NewSqlGo($SqlLink=NULL,$Parameters=NULL){
try{
$sth = $this->dbh->prepare($SqlLink);
$sth->execute($Parameters);
//print_r($sth->errorInfo()); //這裡可以輸出錯誤
} catch (PDOException $e) {
/*連線例外方法
===========================*/
echo "例外";
print_r( $e->errorInfo());
}
//$this->dbh =NULL;//關閉連線
}
這是我寫的一個連線方法 在最上面已經有產生資料庫連線
然而我故意傳入一個錯誤的資料庫 然而只有在TRY 那邊使用 $sth->errorInfo()
才會顯現錯誤語法~
然後在例外處理部分使用$e去接 並把它導向errorInfo()要列印出錯誤
結果
不管是
echo "例外"
或者 $e->errorInfo() 都沒有顯現資料
想說問大大能不能為我解惑~
@CHENG
看看這篇就知道了
http://www.php.net/manual/en/pdo.error-handling.php
基本上 execute() 也有返回 bool 可以讓你判斷有沒有錯 , 但若妳要完全用 Exeception 就要如上述網址去改看看
感謝大大提供全英文資訊~ 哈哈
我對於英文部分真的還要繼續加強~~
不管怎樣 感謝您~~~
哈阿哈哈
以前學校沒教
我是到恆逸上課才學的
我去面試也有被問到這題
考試也有考
借轉喔!!!!
我認為 把SQL injection 丟給PDO prepare處理
並不算懂怎麼過濾
真正的懂 是能用正規式 自己replace
to 曼菲士
確實 PDO prepare 不是徹底防止 sql injection , 但確可以讓程式碼少寫 , 且效能也比較好 , 某些情形下再加以不同的正規式 replace 過濾甚至還要搭配過濾掉資料含有 HTML SCRIPT 的元件來防止寫入資料庫後造成 xss 攻擊 , 這些則是網站開發者隨年齡與經驗必須慢慢去體會的