您好,登錄后才能下訂單哦!
本文主要給大家介紹php內(nèi)置過(guò)濾函數(shù)講義,其所涉及的東西,從理論知識(shí)來(lái)獲悉,有很多書(shū)籍、文獻(xiàn)可供大家參考,從現(xiàn)實(shí)意義角度出發(fā),億速云累計(jì)多年的實(shí)踐經(jīng)驗(yàn)可分享給大家。
0x00:php內(nèi)置過(guò)濾函數(shù)
php有內(nèi)置的函數(shù)用來(lái)防御***,簡(jiǎn)單的介紹幾個(gè)函數(shù)。
魔術(shù)引號(hào)
當(dāng)打開(kāi)時(shí),所有的 '(單引號(hào)),"(雙引號(hào)),\(反斜線)和 NULL 字符都會(huì)被自動(dòng)加上一個(gè)反斜線進(jìn)行轉(zhuǎn)義。這和 addslashes() 作用完全相同。
一共有三個(gè)魔術(shù)引號(hào)指令:
magic_quotes_gpc 影響到 HTTP 請(qǐng)求數(shù)據(jù)(GET,POST 和 COOKIE)。不能在運(yùn)行時(shí)改變。在 PHP 中默認(rèn)值為 on。 參見(jiàn) get_magic_quotes_gpc()。
magic_quotes_runtime 如果打開(kāi)的話,大部份從外部來(lái)源取得數(shù)據(jù)并返回的函數(shù),包括從數(shù)據(jù)庫(kù)和文本文件,所返回的數(shù)據(jù)都會(huì)被反斜線轉(zhuǎn)義。該選項(xiàng)可在運(yùn)行的時(shí)改變,在 PHP 中的默認(rèn)值為 off。 參見(jiàn) set_magic_quotes_runtime() 和 get_magic_quotes_runtime()。
magic_quotes_sybase 如果打開(kāi)的話,將會(huì)使用單引號(hào)對(duì)單引號(hào)進(jìn)行轉(zhuǎn)義而非反斜線。此選項(xiàng)會(huì)完全覆蓋 magic_quotes_gpc。如果同時(shí)打開(kāi)兩個(gè)選項(xiàng)的話,單引號(hào)將會(huì)被轉(zhuǎn)義成 ''。而雙引號(hào)、反斜線 和 NULL 字符將不會(huì)進(jìn)行轉(zhuǎn)義。 如何取得其值參見(jiàn) ini_get()。
mysql_real_escape_string
轉(zhuǎn)義sql語(yǔ)句中使用的字符串中的特殊字符:\x00、\n、\r、\、'、"、\x1a
addslashes()
返回在預(yù)定義字符之前添加反斜杠的字符串,預(yù)定義字符:'、"、\、NULL
看了很多php網(wǎng)站在防sql注入上還在使用ddslashes和str_replace,百度一下"PHP防注入"也同樣在使用他們,實(shí)踐發(fā)現(xiàn)就連mysql_real_escape_string也有***可以繞過(guò)的辦法,如果你的系統(tǒng)仍在用上面三個(gè)方法,建議更好。
用str_replace以及各種php字符替換函數(shù)來(lái)防注入已經(jīng)不用我說(shuō)了,這種“黑名單”式的防御已經(jīng)被證明是經(jīng)不起時(shí)間考驗(yàn)的。
下面給出繞過(guò)addslasher和mysql_real_escape_string的方法(Trick)。
如果你不確定你的系統(tǒng)是否有SQL注入的風(fēng)險(xiǎn),請(qǐng)將下面的下面的DEMO部署到你的云服務(wù)器,如果運(yùn)行結(jié)果相同,那么請(qǐng)參考最后的完美的解決方案。
mysql:
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:
<?php echo "PHP version: ".PHP_VERSION."\n"; mysql_connect('servername','username','password'); mysql_select_db("test"); mysql_query("SET NAMES GBK"); $_POST['username'] = chr(0xbf).chr(0x27).' OR username = username /*'; $_POST['password'] = 'guess'; $username = addslashes($_POST['username']); $password = addslashes($_POST['password']); $sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'"; $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['username']); $password = mysql_real_escape_string($_POST['password']); $sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'"; $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['username']); $password = mysql_real_escape_string($_POST['password']); $sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'"; $result = mysql_query($sql) or trigger_error(mysql_error().$sql); var_dump(mysql_num_rows($result)); var_dump(mysql_client_encoding());
結(jié)果:
PHP version: 5.5.44 int(3) string(6) "latin1" int(3) string(6) "latin1" int(0) string(3) "gbk"
可以看出來(lái)不論是使用addslashes還是mysql_real_escape_string,我都可以利用編碼的漏洞來(lái)實(shí)現(xiàn)輸入任意密碼就能登錄服務(wù)器的注入***?。。?!
0x01:寬字節(jié)注入
盡管現(xiàn)在呼吁所有的程序都使用unicode編碼,所有的網(wǎng)站都使用utf-8編碼,來(lái)一個(gè)統(tǒng)一的國(guó)際規(guī)范。但仍然有很多,包括國(guó)內(nèi)及國(guó)外(特別是非英語(yǔ)國(guó)家)的一些cms,仍然使用著自己國(guó)家的一套編碼,比如gbk,作為自己默認(rèn)的編碼類型。也有一些cms為了考慮老用戶,所以出了gbk和utf-8兩個(gè)版本。
我們就以gbk字符編碼為示范,拉開(kāi)帷幕。gbk是一種多字符編碼,有一個(gè)地方尤其要注意:
通常來(lái)說(shuō),一個(gè)gbk編碼漢字,占用2個(gè)字節(jié)。一個(gè)utf-8編碼的漢字,占用3個(gè)字節(jié)。在php中,我們可以通過(guò)輸出
echo strlen("和");
來(lái)測(cè)試。當(dāng)將頁(yè)面編碼保存為gbk時(shí)輸出2,utf-8時(shí)輸出3。
除了gbk以外,所有ANSI編碼都是2個(gè)字節(jié)。ansi只是一個(gè)標(biāo)準(zhǔn),在不用的電腦上它代表的編碼可能不相同,比如簡(jiǎn)體中文系統(tǒng)中ANSI就代表是GBK。
如上,想繞過(guò)魔術(shù)引號(hào)等限制,有兩種方式:
1.將\前面再加一個(gè)\(或單數(shù)個(gè)即可),變成\\’,這樣\被轉(zhuǎn)義了,’逃出了限制
2.將\去掉。
我們這里的寬字節(jié)注入是利用mysql的一個(gè)特性,mysql在使用GBK編碼的時(shí)候,會(huì)認(rèn)為兩個(gè)字符是一個(gè)漢字(前一個(gè)ascii碼要大于128,才到漢字的范圍)。
這就是mysql的特性,因?yàn)間bk是多字節(jié)編碼,他認(rèn)為兩個(gè)字節(jié)代表一個(gè)漢字,所以%df和后面的\也就是%5c變成了一個(gè)漢字“運(yùn)”,而’逃逸了出來(lái)。再嘗試“%df%df%27”,就不報(bào)錯(cuò)了。因?yàn)?df%df是一個(gè)漢字,%5c%27不是漢字,仍然是\’。
那么mysql怎么判斷一個(gè)字符是不是漢字,根據(jù)gbk編碼,第一個(gè)字節(jié)ascii碼大于128,基本上就可以了。比如我們不用%df,用%a1也可以,%a1%5c他可能不是漢字,但一定會(huì)被mysql認(rèn)為是一個(gè)寬字符,就能夠讓后面的%27逃逸了出來(lái)。
但需要注意的是
gb2312和gbk應(yīng)該都是寬字節(jié)家族的一員,卻不能注入,這歸結(jié)于gb2312編碼的取值范圍。它的高位范圍是0xA1~0xF7,低位范圍是0xA1~0xFE,而\是0x5c,是不在低位范圍中的。所以,0x5c根本不是gb2312中的編碼,所以自然也是不會(huì)被吃掉的。
所以,把這個(gè)思路擴(kuò)展到世界上所有多字節(jié)編碼,我們可以這樣認(rèn)為:只要低位的范圍中含有0x5c的編碼,就可以進(jìn)行寬字符注入。
0x02:寬字符注入的修復(fù)
先調(diào)用mysql_set_charset函數(shù)設(shè)置連接所使用的字符集為gbk,再調(diào)用mysql_real_escape_string來(lái)過(guò)濾用戶輸入。
這個(gè)方式是可行的,但有部分老的cms,在多處使用addslashes來(lái)過(guò)濾字符串,我們不可能去一個(gè)一個(gè)把a(bǔ)ddslashes都修改成mysql_real_escape_string。我們第二個(gè)解決方案就是,將character_set_client設(shè)置為binary(二進(jìn)制)。
只需在所有sql語(yǔ)句前指定一下連接的形式是二進(jìn)制:
mysql_query("SET character_set_connection=gbk, character_set_results=gbk,character_set_client=binary", $conn);
這幾個(gè)變量是什么意思?
當(dāng)我們的mysql接受到客戶端的數(shù)據(jù)后,會(huì)認(rèn)為他的編碼是character_set_client,然后會(huì)將之將換成character_set_connection的編碼,然后進(jìn)入具體表和字段后,再轉(zhuǎn)換成字段對(duì)應(yīng)的編碼。
然后,當(dāng)查詢結(jié)果產(chǎn)生后,會(huì)從表和字段的編碼,轉(zhuǎn)換成character_set_results編碼,返回給客戶端。
所以,我們將character_set_client設(shè)置成binary,就不存在寬字節(jié)或多字節(jié)的問(wèn)題了,所有數(shù)據(jù)以二進(jìn)制的形式傳遞,就能有效避免寬字符注入。
0x03:PDO和MYSQLI
完美解決方案就是使用擁有Prepared Statement機(jī)制的PDO和MYSQLi來(lái)代替mysql_query(注:mysql_query自 PHP 5.5.0 起已廢棄,并在將來(lái)會(huì)被移除):
PDO:
$pdo = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'pass'); $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 }
MYSQLi:
$stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?'); $stmt->bind_param('s', $name); $stmt->execute(); $result = $stmt->get_result(); while ($row = $result->fetch_assoc()) { // do something with $row }
看了以上php內(nèi)置過(guò)濾函數(shù)講義介紹,希望能給大家在實(shí)際運(yùn)用中帶來(lái)一定的幫助。本文由于篇幅有限,難免會(huì)有不足和需要補(bǔ)充的地方,大家可以繼續(xù)關(guān)注億速云行業(yè)資訊板塊,會(huì)定期給大家更新行業(yè)新聞和知識(shí),如有需要更加專業(yè)的解答,可在官網(wǎng)聯(lián)系我們的24小時(shí)售前售后,隨時(shí)幫您解答問(wèn)題的。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。