本文所有內容轉載自月影無痕的博客http://zhangxugg-163-com.iteye.com/blog/1835721#bc2346092,感謝做者的分享php
合理正確使用PDO,能夠基本上防止SQL注入的產生,本文主要回答如下兩個問題:mysql
a、爲何要使用PDO而不是mysql_connect?sql
b、爲什麼PDO能防注入?數據庫
c、使用PDO防注入的時候應該特別注意什麼?框架
1、爲什麼要優先使用PDO?性能
提升相同SQL模板查詢性能編碼
阻止SQL注入spa
2、爲什麼PDO能防SQL注入?3d
爲了完全搞清楚php與mysql server通信的細節,做者特別使用了wireshark抓包進行研究之。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的緣由。
而正確的轉義應該是給mysql Server指定字符集,並將變量發送給MySQL Server完成根據字符轉義。
那麼,如何才能禁止PHP本地轉義而交由MySQL Server轉義呢?
PDO有一項參數,名爲PDO::ATTR_EMULATE_PREPARES ,表示是否使用PHP本地模擬prepare,此項參數默認值未知。並且根據咱們剛剛抓包分析結果來看,php 5.3.6+默認仍是使用本地變量轉,拼接成SQL發送給MySQL Server的,咱們將這項值設置爲false。
可見此次PHP是將SQL模板和變量是分兩次發送給MySQL的,由MySQL完成變量的轉義處理,既然變量和SQL模板是分兩次發送的,那麼就不存在SQL注入的問題了,但須要在DSN中指定charset屬性,如:
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root');
如此,便可從根本上杜絕SQL注入的問題。
3、使用PDO的注意事項
一、若使用php 5.3.6+, 請在在PDO的DSN中指定charset屬性
DSN中指定charset的做用是什麼? 只是告訴PDO, 本地驅動轉義時使用指定的字符集(並非設定mysql server通訊字符集),設置mysql server通訊字符集,還得使用set names <charset>指令。
二、若是使用了PHP 5.3.6及之前版本,設置PDO::ATTR_EMULATE_PREPARES參數爲false(即由MySQL進行變量處理);因Yii框架默認並未設置ATTR_EMULATE_PREPARES的值,請在數據庫配置文件中指定emulatePrepare的值爲false。