本文所有内容转载自月影无痕的博客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。