《PHP學(xué)習(xí):PDO防注入原理分析以及注意事項(xiàng)》要點(diǎn):
本文介紹了PHP學(xué)習(xí):PDO防注入原理分析以及注意事項(xiàng),希望對(duì)您有用。如果有疑問,可以聯(lián)系我們。
我們都知道,只要合理正確使用PDO,可以基本上防止SQL注入的產(chǎn)生,本文主要回答以下兩個(gè)問題:PHP應(yīng)用
為什么要使用PDO而不是mysql_connect?PHP應(yīng)用
為何PDO能防注入?PHP應(yīng)用
使用PDO防注入的時(shí)候應(yīng)該特別注意什么?PHP應(yīng)用
一、為何要優(yōu)先使用PDO?PHP應(yīng)用
PHP手冊(cè)上說得很清楚:PHP應(yīng)用
The query only needs to be parsed (or prepared) once, but can be executed multiple times with the same or different parameters. When the query is prepared, the database will analyze, compile and optimize its plan for executing the query. For complex queries this process can take up enough time that it will noticeably slow down an application if there is a need to repeat the same query many times with different parameters. By using a prepared statement the application avoids repeating the analyze/compile/optimize cycle. This means that prepared statements use fewer resources and thus run faster.PHP應(yīng)用
The parameters to prepared statements don't need to be quoted; the driver automatically handles this. If an application exclusively uses prepared statements, the developer can be sure that no SQL injection will occur(however, if other portions of the query are being built up with unescaped input, SQL injection is still possible).
PHP應(yīng)用
即使用PDO的prepare方式,主要是提高相同SQL模板查詢性能、阻止SQL注入PHP應(yīng)用
同時(shí),PHP手冊(cè)中給出了警告信息PHP應(yīng)用
意思是說,在PHP 5.3.6及以前版本中,并不支持在DSN中的charset定義,而應(yīng)該使用PDO::MYSQL_ATTR_INIT_COMMAND設(shè)置初始SQL, 即我們常用的 set names gbk指令.PHP應(yīng)用
我看到一些程序,還在嘗試使用addslashes達(dá)到防注入的目的,殊不知這樣其實(shí)問題更多, 詳情請(qǐng)看http://www.lorui.com/addslashes-mysql_escape_string-mysql_real_eascape_string.htmlPHP應(yīng)用
還有一些做法:在執(zhí)行數(shù)據(jù)庫(kù)查詢前,將SQL中的select, union, ....之類的關(guān)鍵詞清理掉.這種做法顯然是非常錯(cuò)誤的處理方式,如果提交的正文中確實(shí)包括 the students's union , 替換后將篡改本來的內(nèi)容,濫殺無辜,不可取.PHP應(yīng)用
二、為何PDO能防SQL注入?
請(qǐng)先看以下PHP代碼:PHP應(yīng)用
環(huán)境如下:PHP應(yīng)用
PHP 5.4.7PHP應(yīng)用
Mysql 協(xié)議版本 10PHP應(yīng)用
MySQL Server 5.5.27PHP應(yīng)用
為了徹底搞清楚php與mysql server通訊的細(xì)節(jié),我特別使用了wireshark抓包進(jìn)行研究之,安裝wireshak之后,我們?cè)O(shè)置過濾條件為tcp.port==3306, 如下圖:PHP應(yīng)用
PHP應(yīng)用
如此只顯示與mysql 3306端口的通信數(shù)據(jù),避免不需要的干擾.PHP應(yīng)用
特別要注意的是wireshak基于wincap驅(qū)動(dòng),不支持本地環(huán)回接口的偵聽(即使用php連接本地mysql的辦法是無法偵聽的),請(qǐng)連接其它機(jī)器(橋接網(wǎng)絡(luò)的虛擬機(jī)也可)的MySQL進(jìn)行測(cè)試.PHP應(yīng)用
然后運(yùn)行我們的PHP程序,偵聽結(jié)果如下,我們發(fā)現(xiàn),PHP只是簡(jiǎn)單地將SQL直接發(fā)送給MySQL Server :PHP應(yīng)用
PHP應(yīng)用
其實(shí),這與我們平時(shí)使用mysql_real_escape_string將字符串進(jìn)行轉(zhuǎn)義,再拼接成SQL語句沒有差別(只是由PDO本地驅(qū)動(dòng)完成轉(zhuǎn)義的),顯然這種情況下還是有可能造成SQL注入的,也就是說在php本地調(diào)用pdo prepare中的mysql_real_escape_string來操作query,使用的是本地單字節(jié)字符集,而我們傳遞多字節(jié)編碼的變量時(shí),有可能還是會(huì)造成SQL注入漏洞(php 5.3.6以前版本的問題之一,這也就解釋了為安在使用PDO時(shí),建議升級(jí)到php 5.3.6+,并在DSN字符串中指定charset的原因.PHP應(yīng)用
針對(duì)php 5.3.6以前版本,以下代碼仍然可能造成SQL注入問題:PHP應(yīng)用
原因與上面的分析是一致的.PHP應(yīng)用
而正確的轉(zhuǎn)義應(yīng)該是給mysql Server指定字符集,并將變量發(fā)送給MySQL Server完成根據(jù)字符轉(zhuǎn)義.PHP應(yīng)用
那么,如何才能禁止PHP當(dāng)?shù)剞D(zhuǎn)義而交由MySQL Server轉(zhuǎn)義呢?PHP應(yīng)用
PDO有一項(xiàng)參數(shù),名為PDO::ATTR_EMULATE_PREPARES ,表示是否使用PHP當(dāng)?shù)啬Mprepare,此項(xiàng)參數(shù)默認(rèn)值未知.而且根據(jù)我們剛剛抓包分析結(jié)果來看,php 5.3.6+默認(rèn)還是使用當(dāng)?shù)刈兞哭D(zhuǎn),拼接成SQL發(fā)送給MySQL Server的,我們將這項(xiàng)值設(shè)置為false, 試試效果,如以下代碼:PHP應(yīng)用
紅色行是我們剛加入的內(nèi)容,運(yùn)行以下程序,使用wireshark抓包分析,得出的結(jié)果如下:PHP應(yīng)用
PHP應(yīng)用
PHP應(yīng)用
看到了嗎?這就是神奇之處,可見這次PHP是將SQL模板和變量是分兩次發(fā)送給MySQL的,由MySQL完成變量的轉(zhuǎn)義處理,既然變量和SQL模板是分兩次發(fā)送的,那么就不存在SQL注入的問題了,但必要在DSN中指定charset屬性,如:PHP應(yīng)用
如此,即可從根本上杜絕SQL注入的問題.如果你對(duì)此不是很清楚,可以發(fā)郵件至zhangxugg@163.com, 一起探討.PHP應(yīng)用
三、使用PDO的注意事項(xiàng)PHP應(yīng)用
知道以上幾點(diǎn)之后,我們就可以總結(jié)使用PDO杜絕SQL注入的幾個(gè)注意事項(xiàng):PHP應(yīng)用
1.? php升級(jí)到5.3.6+,生產(chǎn)環(huán)境強(qiáng)烈建議升級(jí)到php 5.3.9+ php 5.4+,php 5.3.8存在致命的hash碰撞漏洞.PHP應(yīng)用
2. 若使用php 5.3.6+, 請(qǐng)?jiān)谠赑DO的DSN中指定charset屬性PHP應(yīng)用
3. 如果使用了PHP 5.3.6及以前版本,設(shè)置PDO::ATTR_EMULATE_PREPARES參數(shù)為false(即由MySQL進(jìn)行變量處理),php 5.3.6以上版本已經(jīng)處理了這個(gè)問題,無論是使用當(dāng)?shù)啬Mprepare還是調(diào)用mysql server的prepare均可.在DSN中指定charset是無效的,同時(shí)set names <charset>的執(zhí)行是必不可少的.PHP應(yīng)用
4. 如果使用了PHP 5.3.6及以前版本, 因Yii框架默認(rèn)并未設(shè)置ATTR_EMULATE_PREPARES的值,請(qǐng)?jiān)跀?shù)據(jù)庫(kù)配置文件中指定emulatePrepare的值為false.PHP應(yīng)用
那么,有個(gè)問題,如果在DSN中指定了charset, 是否還必要執(zhí)行set names <charset>呢?PHP應(yīng)用
是的,不能省.set names <charset>其實(shí)有兩個(gè)作用:PHP應(yīng)用
A.? 告訴mysql server, 客戶端(PHP程序)提交給它的編碼是什么PHP應(yīng)用
B.? 告訴mysql server, 客戶端必要的結(jié)果的編碼是什么PHP應(yīng)用
也便是說,如果數(shù)據(jù)表使用gbk字符集,而PHP程序使用UTF-8編碼,我們?cè)趫?zhí)行查詢前運(yùn)行set names utf8, 告訴mysql server正確編碼即可,無須在程序中編碼轉(zhuǎn)換.這樣我們以u(píng)tf-8編碼提交查詢到mysql server, 得到的結(jié)果也會(huì)是utf-8編碼.省卻了程序中的轉(zhuǎn)換編碼問題,不要有疑問,這樣做不會(huì)產(chǎn)生亂碼.PHP應(yīng)用
那么在DSN中指定charset的作用是什么? 只是告訴PDO, 當(dāng)?shù)仳?qū)動(dòng)轉(zhuǎn)義時(shí)使用指定的字符集(并不是設(shè)定mysql server通信字符集),設(shè)置mysql server通信字符集,還得使用set names <charset>指令.PHP應(yīng)用
我真想不通,一些新的項(xiàng)目,為何不使用PDO而使用傳統(tǒng)的mysql_XXX函數(shù)庫(kù)呢?如果正確使用PDO,可以從根本上杜絕SQL注入,我強(qiáng)烈建議各個(gè)公司的技術(shù)負(fù)責(zé)人、一線技術(shù)研發(fā)人員,要對(duì)這個(gè)問題引起重視,盡可能使用PDO加快項(xiàng)目進(jìn)度和平安質(zhì)量.PHP應(yīng)用
不要再嘗試本身編寫SQL注入過濾函數(shù)庫(kù)了(又繁瑣而且很容易產(chǎn)生未知的漏洞).PHP應(yīng)用
以上便是本文的全部?jī)?nèi)容了,希望小伙伴們能夠好好讀完,十分實(shí)用.PHP應(yīng)用
維易PHP培訓(xùn)學(xué)院每天發(fā)布《PHP學(xué)習(xí):PDO防注入原理分析以及注意事項(xiàng)》等實(shí)戰(zhàn)技能,PHP、MYSQL、LINUX、APP、JS,CSS全面培養(yǎng)人才。
轉(zhuǎn)載請(qǐng)注明本頁網(wǎng)址:
http://www.fzlkiss.com/jiaocheng/12193.html