`
月影无痕
  • 浏览: 1008600 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

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

 
阅读更多

 

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

为什么要使用PDO而不是mysql_connect?

为何PDO能防注入?

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

 

一、为何要优先使用PDO?

PHP手册上说得很清楚:

Prepared statements and stored procedures
Many of the more mature databases 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. Prepared statements 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 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.

 


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).

 

即使用PDO的prepare方式,主要是提高相同SQL模板查询性能、阻止SQL注入

同时,PHP手册中给出了警告信息

Prior to PHP 5.3.6, this element was silently ignored. The same behaviour can be partly replicated with the PDO::MYSQL_ATTR_INIT_COMMAND driver 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 representation as ASCII, such as ISO-8859-1 and UTF-8. 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代码:

<?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, 如下图:

 

 

 

 

如此只显示与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 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抓包分析,得出的结果如下:

 



 

 

看到了吗?这就是神奇之处,可见这次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.4+,php 5.3.8存在致命的hash碰撞漏洞。

 

2. 若使用php 5.3.6+, 请在在PDO的DSN中指定charset属性

3. 如果使用了PHP 5.3.6及以前版本,设置PDO::ATTR_EMULATE_PREPARES参数为false(即由MySQL进行变量处理),php 5.3.6以上版本已经处理了这个问题,无论是使用本地模拟prepare还是调用mysql server的prepare均可。在DSN中指定charset是无效的,同时set names <charset>的执行是必不可少的。

 

4. 如果使用了PHP 5.3.6及以前版本, 因Yii框架默认并未设置ATTR_EMULATE_PREPARES的值,请在数据库配置文件中指定emulatePrepare的值为false。

 

那么,有个问题,如果在DSN中指定了charset, 是否还需要执行set names <charset>呢?

是的,不能省。set names <charset>其实有两个作用:

A.  告诉mysql server, 客户端(PHP程序)提交给它的编码是什么

B.  告诉mysql 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 <charset>指令。

 

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

 

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

 

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

 

 

 

  • 大小: 19.5 KB
  • 大小: 65.2 KB
  • 大小: 76.3 KB
  • 大小: 99.8 KB
14
4
分享到:
评论
12 楼 fanyilong_v5 2014-09-03  
哈哈,现在写的框架就是用的PDO,再说貌似以后7开始的数据库api都是PDO。
11 楼 月影无痕 2014-04-26  
liangjian1024 写道
PDO的效率要略低于mysql_XXX,你可以试试


php新版中,将舍弃掉对mysql的支持。
10 楼 liangjian1024 2014-04-23  
PDO的效率要略低于mysql_XXX,你可以试试
9 楼 月影无痕 2013-05-02  
xiaogui32 写道
我想请问下,您应该有去研究一下phpcms v9的代码吧。如果我们要改造成PDO的要怎么改?改造phpcms/libs/classes/mysql.class.php吗?
但问题是,很多模块没有使用"select * from info where id =? and name = ?" 这样的代替符,在数据层要怎么处理?



我正在实现phpcms v9的PDO数据访问层,正在进行测试。测试成功之后,会发布出来的。
8 楼 xiaogui32 2013-04-30  
我想请问下,您应该有去研究一下phpcms v9的代码吧。如果我们要改造成PDO的要怎么改?改造phpcms/libs/classes/mysql.class.php吗?
但问题是,很多模块没有使用"select * from info where id =? and name = ?" 这样的代替符,在数据层要怎么处理?
7 楼 huangdj 2013-03-26  
这就是神奇之处,可见这次PHP是将SQL模板和变量是分两次发送给MySQL的,由MySQL完成变量的转义处理,既然变量和SQL模板是分两次发送的,那么就不存在SQL注入的问题了
===================================
我比较钝愚,楼主能详细解释一下 分两次就没有注入问题?
$st->bindParam(1,$id);$st->bindParam(2,$name); $st->execute(); 我原理上不明白,感觉是参数化后,数据库进行了怎样执行?我个人认为这个才是防止sql注入关键,请多多指教啊!
6 楼 huangdj 2013-03-26  
这就是神奇之处,可见这次PHP是将SQL模板和变量是分两次发送给MySQL的,由MySQL完成变量的转义处理,既然变量和SQL模板是分两次发送的,那么就不存在SQL注入的问题了
5 楼 fyland 2013-03-26  
月影无痕 写道
fyland 写道
php 5.4+ 哪里不稳定了?能否给出测试代码?


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

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

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

有,但不是“大量”的,当时用最新版的重新编译了一下就没再出现过!
4 楼 月影无痕 2013-03-25  
fyland 写道
php 5.4+ 哪里不稳定了?能否给出测试代码?


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

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

我的php-fpm.log中出现大量seg fault之类的错误,导致我不得不暂停生产环境中使用它。
3 楼 fyland 2013-03-25  
月影无痕 写道
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要好一些。
2 楼 月影无痕 2013-03-25  
fyland 写道
php 5.4+ 哪里不稳定了?能否给出测试代码?


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

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

相关推荐

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

    使用PDO时,防注入的注意事项包括: 1. **始终使用预处理语句**:避免直接拼接SQL字符串,即使你认为输入是安全的,也应使用预处理语句来构建查询,因为未来可能的代码修改可能会引入漏洞。 2. **验证和清理输入**...

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

    在理解PDO防注入原理之前,我们先来探讨为什么应优先使用PDO而非传统的`mysql_connect`。 一、为何优先使用PDO? 1. **预处理语句**:预处理语句是一种数据库模板,允许开发者提前编译SQL语句,然后多次执行,每次...

    SQL注入漏洞演示源代码

    首先,让我们来看看`README.md`文件,它通常包含了项目简介、使用指南和注意事项等内容。在这个案例中,`README.md`可能会详细解释源代码的目的,如何运行示例,以及可能遇到的问题和解决办法。这将帮助我们了解演示...

    PHP防止sql注入小技巧之sql预处理原理与实现方法分析

    5. **安全注意事项**: - 使用预处理语句时,始终确保参数是安全的,最好避免直接使用用户输入的数据。 - 保持良好的编程习惯,如限制数据库用户的权限,避免使用动态SQL构建复杂的查询。 通过理解SQL预处理的...

    第4套PHP面试题

    11. **session和cookie**:了解它们在用户会话管理中的作用,如何设置和获取session及cookie值,以及安全注意事项。 12. **函数和回调**:匿名函数(lambda function或closure),回调函数,以及如何使用call_user_...

    PHP实例开发源码-博卡PHP BICQ系统.zip

    源码中包含的"使用须知.txt"文件很可能是对整个系统的使用指南,包括如何部署、运行以及注意事项等内容。这将帮助开发者快速理解和启动项目,避免在设置过程中遇到问题。建议首先阅读此文件,按照步骤进行操作,确保...

    PHP+SQL考勤系统安全性实现(源代码+论文+答辩PPT+指导书).rar

    指导书作为项目实施的指南,详述了系统开发过程中的步骤、注意事项以及可能出现的问题及解决方法,对开发者具有很高的参考价值。 综上所述,《PHP+SQL考勤系统安全性实现》是一个全面的实战项目,不仅提供了实际的...

    chapter03.rar

    - Cookie:介绍Cookie的工作原理,学习setcookie函数的使用及注意事项。 8. 面向对象编程(OOP): - 类与对象:了解类的定义、对象的创建和实例化,以及属性和方法的概念。 - 访问控制:理解public、private、...

    PHP实例开发源码—php开源学习型框架CMVC.zip

    通过仔细阅读和分析【使用须知.txt】,可以获取更多关于CMVC框架的使用指导和注意事项。同时,132678941017573725可能是框架的某个关键文件,可能包含核心功能的实现或框架的安装指南。深入研究这个文件,可以帮助...

    PHP实例开发源码—PPVOD 皮皮PHP视频系统.zip

    7. 安全机制:为了保护视频内容和用户信息,系统需要实施安全措施,如防止SQL注入、XSS攻击,以及使用HTTPS加密传输。 8. API集成:PPVOD可能提供了API接口,允许其他应用或服务与其交互,例如社交媒体分享、支付...

    PHP实例开发源码—搜书网投票系统PHP版.zip

    1. **使用须知.txt** - 这个文件很可能包含关于如何安装、配置和运行这个投票系统的指南,包括环境要求、数据库设置、文件结构解析以及可能的注意事项。用户在使用之前应该首先阅读此文件,以确保正确地部署和操作...

    PHP实例开发源码—Timely在线客服系统开源版.zip

    1. **使用须知.txt**:这是一个文本文件,通常包含了项目的基本信息,如安装指南、系统需求、许可证信息、使用限制以及开发者可能需要注意的重要事项。在开始使用Timely在线客服系统之前,务必仔细阅读此文件,以...

    PHP实例开发源码-宗师堂软文发稿平台营销系统.zip

    通过分析这个压缩包内的"使用须知.txt",你将能够了解系统运行的具体步骤和注意事项,而"132678309360267427"可能是数据库连接配置文件、系统配置文件或其他重要文件。深入研究这些文件,可以更全面地理解整个系统的...

    PHP100论坛

    4. **session与cookie**:为了实现用户登录状态的保持,源码中会涉及session和cookie的使用,了解它们的工作原理和安全注意事项。 5. **表单处理与验证**:论坛的注册、登录、发帖等功能都需要处理用户提交的表单...

    PHP实例开发源码-牛人偷站工具 php版.zip

    1. **使用须知.txt**:这是一个文本文件,很可能包含了关于如何使用这个“偷站工具”的具体步骤、注意事项以及可能遇到的问题和解决方案。使用前应该仔细阅读此文件,确保正确且合法地使用源代码,避免侵犯他人的...

    PHP实例开发源码—盈盈通 JSP 网络聊天软件 UTF-8.zip

    尽管压缩包中的"使用须知.txt"文件未给出具体信息,但通常它会包含如何解压和运行项目的说明,以及可能的依赖和注意事项。至于"132685149496944931"可能是另一个文件名,但无法确定其具体内容,可能是数据库配置文件...

    PHP实例开发源码—ecShop PHP网上商城系统源码.zip

    1. **使用须知.txt**:这个文件可能是ecShop系统使用前的注意事项或安装指南,通常会包含系统配置要求、安装步骤、数据库设置、环境准备等关键信息,对于初学者或者初次接触ecShop的人来说至关重要。 2. **...

    PHP实例开发源码—php西西美图站源码.zip

    【PHP实例开发源码—php西西美图站源码.zip】是一个包含PHP源代码的压缩包,专门用于展示PHP在构建网站,尤其是图片分享站点...在使用前,务必阅读“使用须知.txt”,了解源码的使用规则和注意事项,避免不必要的问题。

Global site tag (gtag.js) - Google Analytics