PHP防SQL注入不要再用addslashes和mysql_real_escape_string了

WBOY
發布: 2016-08-08 09:30:02
原創
1036 人瀏覽過

部落客熱衷各種網路科技,常囉嗦,時常伴隨強迫症,常更新,覺得文章對你有幫助的可以關注我。 轉載請註明"深藍色的鐮刀"


看了很多PHP網站在防SQL注入上還在使用addslashes和str_replace,百度一下"PHP防注入"也同樣在使用他們,實踐發現就連mysql_real_escape_string也有駭客可以繞過的辦法,如果你的系統仍在用上面三個方法,那麼我的這篇博文就有了意義,以提醒所有後來者繞過這個坑。

出於為後人栽樹而不是挖坑的考慮,給出PHP以及MYSQL的版本信息,以免將來“問題”不再是“問題”了。

用str_replace以及各種php字元替換函數來防注入已經不用我說了,這種「黑名單」式的防禦已經被證明是經不起時間考驗的。

下面給出繞過addslasher和mysql_real_escape_string的方法(Trick)。

5 -log下該Trick已經被修復了,但仍然沒有確切地解決注入問題,介於許多公司的系統仍在使用Mysql5.0,我建議立刻做出改進,這點也是我《也說說幾種讓程式設計師快速提高能力的方法 》中提到的一個十分重要的點。

注意:如果你不確定你的系統是否有

注意:如果你不確定你的系統是否有對執行的SQL相同,那麼請參考最後的完美的解決方案。

PHP:

mysql> select version();
+---------------------+
| version()           |
+---------------------+
| 5.0.45-community-ny |
+---------------------+
1 row in set (0.00 sec)
mysql> create database test default charset GBK;
Query OK, 1 row affected (0.00 sec)
mysql> use test;
Database changed
mysql> CREATE TABLE users (
    username VARCHAR(32) CHARACTER SET GBK,
    password VARCHAR(32) CHARACTER SET GBK,
    PRIMARY KEY (username)
);
Query OK, 0 rows affected (0.02 sec)
mysql> insert into users SET username='ewrfg', password='wer44';
Query OK, 1 row affected (0.01 sec)
mysql> insert into users SET username='ewrfg2', password='wer443';
Query OK, 1 row affected (0.01 sec)
mysql> insert into users SET username='ewrfg4', password='wer4434';
Query OK, 1 row affected (0.01 sec)=
登入後複製

結果:

<?php
echo "PHP version: ".PHP_VERSION."\n";

mysql_connect(&#39;servername&#39;,&#39;username&#39;,&#39;password&#39;);
mysql_select_db("test");
mysql_query("SET NAMES GBK");

$_POST[&#39;username&#39;] = chr(0xbf).chr(0x27).&#39; OR username = username /*&#39;;
$_POST[&#39;password&#39;] = &#39;guess&#39;;

$username = addslashes($_POST[&#39;username&#39;]);
$password = addslashes($_POST[&#39;password&#39;]);
$sql = "SELECT * FROM  users WHERE  username = &#39;$username&#39; AND password = &#39;$password&#39;";
$result = mysql_query($sql) or trigger_error(mysql_error().$sql);

var_dump(mysql_num_rows($result));
var_dump(mysql_client_encoding());

$username = mysql_real_escape_string($_POST[&#39;username&#39;]);
$password = mysql_real_escape_string($_POST[&#39;password&#39;]);
$sql = "SELECT * FROM  users WHERE  username = &#39;$username&#39; AND password = &#39;$password&#39;";
$result = mysql_query($sql) or trigger_error(mysql_error().$sql);

var_dump(mysql_num_rows($result));
var_dump(mysql_client_encoding());

mysql_set_charset("GBK");
$username = mysql_real_escape_string($_POST[&#39;username&#39;]);
$password = mysql_real_escape_string($_POST[&#39;password&#39;]);
$sql = "SELECT * FROM  users WHERE  username = &#39;$username&#39; AND password = &#39;$password&#39;";
$result = mysql_query($sql) or trigger_error(mysql_error().$sql);

var_dump(mysql_num_rows($result));
var_dump(mysql_client_encoding());
登入後複製
我可以看出來不論是使用addslashes還是mysql_real_escape_string,我都可以利用編碼的漏洞來實現輸入任意密碼就能登入伺服器的注入攻擊! ! ! !

(攻擊的原理我就不多說了,有興趣的同學可以研究下字符編碼中單字節和多字節的問題)

注意:第三個mysql_real_escape_string之所以能夠防注入是因為mysql_escape_string本身並沒辦法判斷目前的編碼,必須同時指定服務端的編碼和客戶端的編碼,加上就能防編碼問題的注入了。雖然是可以一定程度上防止SQL注入,但還是建議以下的完美解決方案。


完美解決方案就是使用擁有Prepared Statement機制的PDO和MYSQLi來代替mysql_query(註:mysql_query自PHP 5.5.0 起已廢棄,並在將來會被移除:
PHP version: 5.2.5
int(3)
string(6) "latin1"
int(3)
string(6) "latin1"
int(0)
string(3) "gbk" 
登入後複製
MYSQLi:
$pdo = new PDO(&#39;mysql:dbname=dbtest;host=127.0.0.1;charset=utf8&#39;, &#39;user&#39;, &#39;pass&#39;);

$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) {
    // do something with $row
}
登入後複製

以上就介紹了PHP防SQL注入不要再用addslashes和mysql_real_escape_string了,包括了方面的內容,希望對PHP教程有興趣的朋友有幫助。

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!