PHP 開發中有效防禦 SQL 注入攻擊有哪些好方法?
沒有編譯就沒有注入,避免提交上來的數據被編譯就可以了,參數綁定就是避免提交數據被編譯的方法。
可以用預處理參數化查詢或手動轉義參數來防禦SQL注入,不僅僅PHP開發中如此,其他語言中大多也是如此.甭管什麼Query Builder還是ORM,最終還是要轉換成SQL交給資料庫執行,所以它們底層同樣需要通過預處理參數化查詢或手動轉義參數來避免被SQL注入,沒有任何黑科技.
比如medoo.php這個PHP實現的Query Builder,就是通過PDO::quote($param)手動轉義參數,然後通過exec/query來執行SQL,其並沒有使用預處理參數化查詢(prepare($sql)/execute(array($param)).
把所有參數當成字元串來處理是必要的.也就是說intval($_GET["id"])雖然能把$_GET["id"]轉成整數,但存在一個問題,如果$_GET["id"]是一個大於PHP_INT_MAX的數,intval將不能正常處理.而資料庫中的unsigned bigint(20)的大小相當於2倍64位PHP_INT_MAX的大小,所以有必要把$_GET["id"]當做字元串來處理.另外,大於PHP_INT_MAX的數的運算應該使用PHP內置的bcmath或者gmp擴展.
下面分別演示pdo_mysql和mysqli的SQL IN()語句的預處理參數化查詢:
&prepare($sql);
$stmt-&>execute($ids); //所有id都當做字元串處理,值傳遞.
return $stmt-&>fetchAll(PDO::FETCH_ASSOC);
}
// var_export(app_in_mysqli(array(1, 3, 5))); 要求使用PHP內置的mysqlnd驅動
function app_in_mysqli(array $ids) {
global $app;
$db = app_mysql();
$table = $app["db_prefix"]."post";
$place_holders = app_place_holders($ids);
$sql = "SELECT * FROM `{$table}` WHERE `id` IN ({$place_holders})";
$stmt = $db-&>prepare($sql);
//MySQLi自動化"引用綁定"參數(因為mysqli的execute不像pdo的execute支持參數數組傳遞,所以顯得麻煩些)
$params = array_merge(array(str_repeat("s", count($ids))), $ids); //array("sss", 1, 3, 5)
foreach($params as $k =&> $v) { $params[$k] = $params[$k]; } //因為bind_param要求傳遞引用.
call_user_func_array(array($stmt, "bind_param"), $params); //相當於$stmt-&>bind_param("sss", $ids[0], $ids[1], $ids[2]);
$stmt-&>execute();
return $stmt-&>get_result()-&>fetch_all(MYSQLI_ASSOC);
}
用tail -f /path/to/mysqld/general_log查看MySQL的SQL日誌可見:
Prepare SELECT * FROM `app_post` WHERE `id` IN (?,?,?)
Execute SELECT * FROM `app_post` WHERE `id` IN ("1","3","5")
Close stmt
使用PDO或者MySQLi,有很多封裝好的方便的Class。例如使用PHP-PDO-MySQL-Class · GitHub(這個Class使用上比較類似Python的MySQLdb)的話,這樣就是安全的:
&query("SELECT * FROM fruit WHERE name IN (?)",array($_GET["pm1"],$_GET["pm2"]));
$DB-&>query("SELECT * FROM users WHERE name=? and password=?",array($_GET["name"],$_GET["pw"]));
?&>
直接拼接字元串則是危險的:
&query("SELECT * FROM fruit WHERE name IN ("".$_GET["pm1"]."","".$_GET["pm2"]."")");
$DB-&>query("SELECT * FROM users WHERE name="".$_GET["name"]."" and password="".$_GET["pw"].""");
?&>
PDO參數綁定的原理是將命令與參數分兩次發送到MySQL,MySQL就能識別參數與命令,從而避免SQL注入(在參數上構造命令)。
mysql在新版本PHP中已經預廢棄,使用的話會拋出錯誤,現在建議使用MySQLi或者MySQL_PDO。一律不能直接使用外來的參數,不要直接構造查詢語句,使用statement進行參數填充等等
prepare bind 一下即可。
給兩個辦法,匈牙利命名法、注入嘗試探測
別瞧不起匈牙利命名法,這貨的原意可不是你以為的傻瓜一樣在變數名後加上類型名。真正的做法是:未做escape過濾的字元串命令按照你自己的習慣命名,escape後的字元串加上類似_f或_ss這樣的suffix。習慣後在寫代碼的時候自然就會要求傳入的字元串都是過濾過的。
注入嘗試探測則比較簡單:作為黑客來說如果需要進行攻擊,會進行多次注入嘗試,期間幾乎可以肯定會構造出一個無效的sql語句,執行時就會報錯了。自己寫個資料庫的query函數進行封裝,如果發現有執行無效的sql語句則通過郵件或其他形式進行報警。
當然…擔心SQL注入,都是用拼接字元串做SQL查詢的土鱉程序員。正常的程序員會去找個ORM用。防SQL注入最好的方法就是千萬不要自己拼裝SQL命令和參數, 而是用PDO的prepare和bind.
原理就在於要把你的SQL查詢命令和傳遞的參數分開: &> prepare的時候, DB server會把你的SQL語句解析成SQL命令. &> bind的時候, 只是動態傳參給DB Server解析好的SQL命令.其他所有的過濾特殊字元串這種白名單的方式都是浮雲.我採用的方法是:
1.對團隊成員進行安全培訓,找出最常見的攻擊方法逐個指導代碼內屏蔽方式
2.用nginx之類的反向代理進行url參數過濾,基本能擋住90%的攻擊3.對磁碟文件進行高度許可權設定4.計劃任務的腳本對程序源代碼定期(比如每小時)執行遍歷搜索,一般水平的攻擊,都逃不過這個排查5.對nginx過濾出的危險url進行程序自動分析,對於超過閾值的ip直接防火牆屏蔽1、不要隨意開啟生產環境中Webserver的錯誤顯示。
2、永遠不要信任來自用戶端的變數輸入,有固定格式的變數一定要嚴格檢查對應的格式,沒有固定格式的變數需要對引號等特殊字元進行必要的過濾轉義。3、使用預編譯綁定變數的SQL語句。4、做好資料庫帳號許可權管理。5、嚴格加密處理用戶的機密信息。來自 「Web安全之SQL注入攻擊技巧與防範」。
過濾輸入變數類型限制採用PDO操作資料庫
簡單來說,僅僅對於PHP程序的SQL注入,對輸入輸出的數據進行安全過濾是最主要的方法。當然說起來簡單,真正要做到很複雜,要考慮的細節和因素很多,包括編碼、類型、前後邏輯等等,一個處理不慎,反而會弄巧成拙。因此要做到程序儘可能的安全,需要程序員具有一定的安全意識和知識,從程序的最底層構建上就把安全因素考慮進去。個人認為國內PHP安全圈的水準還是相當高的,各方面的資料也很多,推薦兩個網站,裡面有很多相關的資料,有興趣的可以參考學習下:http://80vul.comhttp://bbs.wolvez.org
所有輸入輸出都進行過濾,資料庫查詢用參數化查詢。
http://www.freebuf.com參考 PHP 的官方擴展 Filter http://au2.php.net/manual/zh/book.filter.php
防SQL注入最好的方法是不要自己拼裝SQL命令和參數, 而是用PDO的prepare和bind.原理就在於要把你的SQL查詢命令和傳遞的參數分開。希望能幫助到你。
添加php探針,實時防禦。更多詳情關註:OneASP - RASP安全自適應平台|雲安全解決方案|安全可視化SaaS平台
通常有一下幾種方法:
(1)輸入驗證和過濾
(2)預處理Sql語句(3)採用存儲過程(4)輸入白名單其他的安全建議:資料庫最小特權連接, 安全編碼才是王道!嚴格過濾 $_post $_get $cookie $request 強制 自己構造的sql 使用 pdo
PDO預處理,整形傳值intval(),配置好資料庫用戶許可權,
get傳參避免字元串,對參數強行轉換,intval之類的
您好,關於php開發中最有效地防禦方式可以參考我的文章,傳送門php最有效防禦sql注入攻擊的方式-PHPChina開發者社區-PHP開發者社區我的博客http://www.fkgeek.com/
推薦閱讀:
※同時學三門編程語言是什麼體驗?
※你會在框架中用到orm嗎?
※求助。php無限極分類。遞歸。怎麼數出來。每一個分類下面的小分類個數啊。希望有個demo?謝謝!?