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

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

 

我们都知道,只要合理正确使用PDO,可以基本上防止SQL注入的产生,本文主要回答以下两个问题:

为何PDO能防注入?

使用PDO防注入的时候应该特别注意什么?

 

一、为何PDO能防SQL注入?
请先看以下PHP代码:

<?php

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

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

 

$id = 21;

$name = 'zhangsan';

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

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

 

$st->execute();

$st->fetchAll();

?>

 

环境如下:

PHP 5.4.7

Mysql 协议版本 10

MySQL Server 5.5.27

 

为了彻底搞清楚php与mysql server通讯的细节,我特别使用了wireshark抓包进行研究之,安装wireshak之后,我们设置过滤条件为tcp.port==3306, 如下图:
PDO防流入原理分析以及使用PDO的注意事项
 

 

 

 

如此只显示与mysql 3306端口的通信数据,避免不必要的干扰。

特别要注意的是wireshak基于wincap驱动,不支持本地环回接口的侦听(即使用php连接本地mysql的方法是无法侦听的),请连接其它机器(桥接网络的虚拟机也可)的MySQL进行测试。

 

然后运行我们的PHP程序,侦听结果如下,我们发现,PHP只是简单地将SQL直接发送给MySQL Server :

 


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

 

 

 

其实,这与我们平时使用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 1=1 /*"; 

$query = "SELECT * FROM info WHERE name = ?"; 

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

$stmt->execute(array($var)); 

 

原因与上面的分析是一致的。

 

而正确的转义应该是给mysql Server指定字符集,并将变量发送给MySQL Server完成根据字符转义。

 

那么,如何才能禁止PHP本地转义而交由MySQL Server转义呢?

PDO有一项参数,名为PDO::ATTR_EMULATE_PREPARES ,表示是否使用PHP本地模拟prepare,此项参数默认值未知。而且根据我们刚刚抓包分析结果来看,php 5.3.6+默认还是使用本地变量转,拼接成SQL发送给MySQL Server的,我们将这项值设置为false, 试试效果,如以下代码:

<?php

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

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

 

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

$id = 21;

$name = 'zhangsan';

 

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

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

$st->execute();

$st->fetchAll();

?>

 

红色行是我们刚加入的内容,运行以下程序,使用wireshark抓包分析,得出的结果如下:
PDO防流入原理分析以及使用PDO的注意事项
 


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

 

看到了吗?这就是神奇之处,可见这次PHP是将SQL模板和变量是分两次发送给MySQL的,由MySQL完成变量的转义处理,既然变量和SQL模板是分两次发送的,那么就不存在SQL注入的问题了,但需要在DSN中指定charset属性,如:

$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root');

 

如此,即可从根本上杜绝SQL注入的问题。如果你对此不是很清楚,可以发邮件至zhangxugg@163.com, 一起探讨。

 

二、使用PDO的注意事项

知道以上几点之后,我们就可以总结使用PDO杜绝SQL注入的几个注意事项:

1.  php升级到5.3.6+,生产环境强烈建议升级到php 5.3.9,php 5.3.8有致命的hash碰撞漏洞,php 5.4+目前又相当不稳定(反复测试的结论,不建议在生产环境中使用)

 

2. 在PDO的DSN中指定charset属性

3. 最重要的要点,设置PDO::ATTR_EMULATE_PREPARES参数为false。

 

4. Yii框架默认并未设置ATTR_EMULATE_PREPARES的值,请在数据库配置文件中指定emulatePrepare的值为false。

 

如果图片丢失,可以发邮件至zhangxugg@163.com, 索取PDF版本。

 

我真想不通,一些新的项目,为何不使用PDO而使用传统的mysql_XXX函数库呢?如果正确使用PDO,可以从根本上杜绝SQL注入,我强烈建议各个公司的技术负责人、一线技术研发人员,要对这个问题引起重视,尽可能使用PDO加快项目进度和安全质量。

 

不要再尝试自己编写SQL注入过滤函数库了(又繁琐而且很容易产生未知的漏洞)。

 

 

 

1 楼 fyland 昨天  
php 5.4+ 哪里不稳定了?能否给出测试代码?
2 楼 月影无痕 昨天  
fyland 写道
php 5.4+ 哪里不稳定了?能否给出测试代码?


和APC, libmemcached模块兼容性很不好

另一方面php-fpm.log日志中,也出现大量报seg fault 之类错误,这时前端nginx就报502之类的错误。
3 楼 fyland 昨天  
月影无痕 写道
fyland 写道
php 5.4+ 哪里不稳定了?能否给出测试代码?


和APC, libmemcached模块兼容性很不好

另一方面php-fpm.log日志中,也出现大量报seg fault 之类错误,这时前端nginx就报502之类的错误。

APC不太清楚,因为看过几个关于APC、xcache、eAccelerator的测试,貌似eAccelerator快一点,所有一直都是用eAccelerator。倒是libmemcached一直在用,在PHP 5.4.1X中没出现过什么问题。
你所说的seg fault错误不知道是不是这个bug导致的https://bugs.php.net/bug.php?id=62836,如果是的话,这个BUG早已修复。
PHP 5.4相比PHP 5.3更加激进,除了新增了一些语法特性外,更好的支持面向对象。更重要的是,正是因为激进,不再兼容一些老的特性,性能有很大的提升,而内存使用率略有减小。官方的说法是:
“许多内部结构已变得更小或完全消失,从而在大型 PHP 应用程序中可节省 20-50% 的内存。通过各种优化使性能提高 10-30%(主要取决于代码执行的操作),这些优化包括内联各种常见代码路径、将 $GLOBALS 添加到 JIT、“@”操作符运算更快、添加了运行时类/函数/常量缓存、运行时字符串常量现在被拘留、通过预先计算的散列更快地访问常量、空数组速度更快并使用更少内存、unserialize() 和 FastCGI 请求处理速度更快,以及在整个代码中进行更多的内存和性能调整。
      例如,早期的一些测试表明,Zend Framework 在 5.4 中运行速度提高 21% 并且内存使用减少 23%,而 Drupal 内存使用减少 50% 并且运行速度大约提高 7%。”

我去年自己的测试数据虽然没有像官方说的那样漂亮,但在性能和内存使用上 PHP5.4 确实比PHP5.3要好一些。
4 楼 月影无痕 13 小时前  
fyland 写道
php 5.4+ 哪里不稳定了?能否给出测试代码?


看来朋友你对这个新版本研究还是相当多的,你这么一说,我倒是很有兴趣也想反复测试一下,尝试将其应用到生产环境中。

那么,针对php 5.4+, 你的php-fpm.log中是否发现过大量错误日志?

我的php-fpm.log中出现大量seg fault之类的错误,导致我不得不暂停生产环境中使用它。
5 楼 fyland 2 小时前  
月影无痕 写道
fyland 写道
php 5.4+ 哪里不稳定了?能否给出测试代码?


看来朋友你对这个新版本研究还是相当多的,你这么一说,我倒是很有兴趣也想反复测试一下,尝试将其应用到生产环境中。

那么,针对php 5.4+, 你的php-fpm.log中是否发现过大量错误日志?

我的php-fpm.log中出现大量seg fault之类的错误,导致我不得不暂停生产环境中使用它。

有,但不是“大量”的,当时用最新版的重新编译了一下就没再出现过!