PDO防注入原理分析以及使用PDO的注意事項

WBOY
發布: 2016-08-08 09:30:03
原創
893 人瀏覽過

我們都知道,只要合理正確使用PDO,可以基本上防止SQL注入的產生,本文主要回答以下兩個問題:

為什麼要使用PDO而不是mysql_connect?

為何PDO能防注入?

使用PDO防注入的時候應該特別注意什麼?

一、為何要優先使用PDO?

PHP手冊上說得很清楚:

Prepared statements and stored procedures the medu support the concept of prepared statements. What are they? They can be thought of as a kind of compiled template for the SQL that an application wants to run, that can be customized using variable parameters. offer two major benefits: 
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 iscut ize the same or different parameters. When the query iscut ize, for base, 藍色, co​​yumi ​​U​​dry is​​dimize the base, Nformi​​dim ​​ity), Nizery iscuting for un​​y 你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 acreed statement the cycle. 
This means that prepared statements use fewer resources and thus run faster.

The parameters to prepared statements don't need to be quoive the driver autop. 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).即使用PDO的prepare方式,主要是

提高相同SQL模板中給了警告訊息

Prior to PHP 5.3.6, this element was silently ignored. The same behaviour can be partly replicated with the PDOMY_MATT​​o option, as the following example shows.
Warning

The method in the below example can only be used with character sets that share the same lower 7 bit represen. Users using character sets that have different representations (such as UTF-16 or Big5) must

 use the 

charset option provided in PHP 5.3.6 and later versions.

意思是說,在PHP 5.3.6及以前版本中,並不支援DSN中的charset定義,而應該使用PDO::MYSQL_ATTR_INIT_COMMAND設定初始SQL, 即我們常用的 set names gbk指令。

我看到一些程序,還在嘗試使用addslashes達到防注入的目的,殊不知這樣其實問題更多, 詳情請看http://www.lorui.com/addslashes-mysql_escape_string-mysql_real_eascape_string.html


有一些做法:在執行資料庫查詢前,將SQL中的select, union, ....之類的關鍵字清理掉。這種做法顯然是非常錯誤的處理方式,如果提交的正文中確實包含 the students's union , 替換後將篡改本來的內容,濫殺無辜,不可取。

二、為何PDO能防SQL注入?

請先看以下PHP代碼:

$pdo = new PDO("mysql:host=192.168.0.1;dbname=test.1;

$st = $pdo->prepare("select * from info where id =? and name = ?");

zhangsan

';

$st->bindParam(1,$id);

$st->bindParam(2,$name);

$st->execute();

$st->execute(); $st->fetchAll();

?>

環境如下:

PHP 5.5.27

為了徹底搞清楚php與mysql server通訊的細節,我特別使用了wireshark抓包進行研究之,安裝wireshak之後,我們設定過濾條件為tcp.port==3306, 如下圖:


如此只顯示與mysql 3306埠的通訊數據,避免不必要的干擾。

特別要注意的是wireshak基於wincap驅動,不支援本地環回介面的偵聽(即使用php連接本地mysql的方法是無法偵聽的),請連接其它機器(橋接網路的虛擬機器也可)的MySQL進行測試。

然後運行我們的PHP程序,偵聽結果如下,我們發現,PHP只是簡單地將SQL直接發送給MySQL Server :

,這與實相🜎我們平常使用mysql_real_escape_string將字串進行轉義,再拼接成SQL語句沒有差別(只是由PDO本地驅動完成轉義的),顯然這種情況下還是有可能造成SQL注入的,也就是說在php本地調用pdo prepare中的mysql_real_escape_string來操作query,使用的是本地單字元組,而我們傳遞多位元組編碼的變數時,有可能還是會造成SQL注入漏洞(php 5.3.6以前版本的問題之一,這也解釋了為何在使用PDO時,建議升級到php 5.3.6+,並在DSN字串中指定charset的原因。


針對php 5.3.6先前版本,以下程式碼仍可能造成SQL問題:


$pdo->query('SET NAMES GBK'); 

$var = chr(0xbf) . chr(0x27)OR. $ query = "SELECT * FROM info WHERE name = ?"; 

$stmt = $pdo->prepare($query);

原因與上面的分析是一致的。 PHP本地轉義而交由MySQL Server轉義呢?我們剛剛抓包分析結果來看,php 5.3.6+預設還是使用本地變數轉,拼接成SQL傳送給MySQL Server的,我們將這項值設為false, 試試看效果,如以下程式碼:

$pdo = new PDO("mysql:host=192.168.0.1;dbname=test;","root");

$pdo->setAttribute(P

.

$st = $pdo->prepare("select * from info where id =? and name = ?");

=$a$id5;

$st->bindParam(1,$id);

$st->bindParam(2,$name);

$st->execute();

$st->execute(); ();

?>

紅色行是我們剛加入的內容,執行以下程序,使用wireshark抓包分析,得出的結果如下:

看到了嗎?這就是神奇之處,可見這次PHP是將SQL模板和變數是分兩次發送給MySQL的,由MySQL完成變數的轉義處理,既然變數和SQL模板是分兩次發送的,那就不存在SQL注入的問題了,但需要在DSN中指定charset屬性,如:


$pdo = new PDO('mysql:host=localhost;dbname=test

;charset=utf8com );

如此,即可從根本上杜絕SQL注入的問題。如果你對此不是很清楚,可以寄電子郵件至zhangxugg@163.com,

 一起探討。

三、使用PDO的注意事項知道以上幾點之後,我們就可以總結使用PDO杜絕SQL注入的幾個注意事項:

1.  php升級到5.3.生產環境強烈建議升級到php 5.3.9+ php 5.4+,php 5.3.8有致命的hash碰撞漏洞。

 

2. 若使用php 5.3.6+, 請在PDO的DSN中指定charset屬性3. 如果使用了PHP 5.3.6及以前版本,設定檔為false(即由MySQL進行變數處理),php 5.3.6以上版本已經處理了這個問題,無論是使用本地模擬prepare或是呼叫mysql server的prepare皆可。在DSN中指定charset是無效的,同時set names 的執行是必不可少的。

 

4. 
如果使用了PHP 5.3.6及以前未設定的資料庫在設定檔中指定emulatePrepare的值為false。

那麼,有個問題,如果在DSN中指定了charset, 是否還需要執行set names 呢?

是的,不能省。 set names 其實有兩個作用:

A.  告訴mysql server, 客戶端(PHPM)提交給server, 客戶端所需要的結果的編碼是什麼

也就是說,如果資料表使用gbk字元集,而PHP程式使用UTF-8編碼,我們在執行查詢前執行set names utf8, 告訴mysql server正確編碼即可,無須在程式中編碼轉換。這樣我們以utf-8編碼提交查詢到mysql server, 得到的結果也會是utf-8編碼。省卻了程式中的轉換編碼問題,不要有疑問,這樣做不會產生亂碼。 那麼在DSN中指定charset的作用是什麼? 只是告訴PDO, 本地驅動轉義時使用指定的字元集(並不是設定mysql server通訊字元集),設定mysql server通訊字元集,還得使用set names 指令。

如果圖片遺失,可以寄email至zhangxugg@163.com,

 索取PDF版本。

我真想不通,一些新的項目,為何不使用PDO而使用傳統的mysql_XXX函數庫呢?如果正確使用PDO,可以從根本上杜絕SQL注入,我強烈建議各家公司的技術負責人、第一線技術研發人員,要對這個問題引起重視,盡可能使用PDO加快專案進度和安全品質。

不要再嘗試自己寫SQL注入濾波函式庫了(又繁瑣而且很容易產生未知的漏洞)。

以上就介紹了PDO防注入原理分析以及使用PDO的注意事項,包括了方面的內容,希望對PHP教程有興趣的朋友有所幫助。

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板