当前位置: 首页 > 编程日记 > 正文

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注入过滤函数库了(又繁琐而且很容易产生未知的漏洞)。

转载于:https://www.cnblogs.com/hubing/p/3158321.html

相关文章:

LSGO软件技术团队招新 线下组队学习

团队招新 LSGO软件技术团队&#xff08;Dreamtech算法组&#xff09;成立于2010年09月&#xff0c;团队主要从事地理信息系统、管理信息系统、计算机视觉等领域的应用开发&#xff0c;团队同时具有培养学生的重要职能&#xff0c;毕业学生分布在IBM、百度、阿里、腾讯、京东、…

java spring 配置文件_[Java教程]Spring配置文件

[Java教程]Spring配置文件02016-03-19 00:00:08Spring配置文件是集成了Spring框架的项目的核心&#xff0c;引擎从哪里开始&#xff0c;中间都执行了哪些操作&#xff0c;小谈一下它的执行流程。容器先是加载web.接着是applicationContext.一种方法是加入ContextLoaderServlet这…

王子朝:一种高效且容错的方法用于协作车辆定位

王子朝是华北电力大学计算机系大四的学生&#xff0c;Dreamtech成员&#xff0c;参加了多期Dreamtech与Datawhale联合组织的组队学习活动&#xff0c;现保送西安电子科技大学深造。 这篇图文是他在线下组队学习时&#xff0c;为大家分享自己所看论文的总结。 希望参与我们组队…

python文件句柄_Python文件操作

classfile(object):def close(self): #real signature unknown; restored from __doc__关闭文件"""close() -> None or (perhaps) an integer. Close the file.Sets data attribute .closed to True. A closed file cannot be used forfurther I/O operation…

XML简单的增改删操作

XML文件的简单增改删&#xff0c;每一个都可以单独拿出来使用。 新创建XML文件&#xff0c;<?xmlversion"1.0"encoding"utf-8"?> <bookstore> <bookgenre"fantasy"ISBN"2-3631-4"> <title>Oberons Legacy&l…

javascript推荐书籍

WEB前端研发工程师&#xff0c;在国内算是一个朝阳职业&#xff0c;这个领域没有学校的正规教育&#xff0c;大多数人都是靠自己自学成才。本文主要介绍自己从事web开发以来(从大二至今)看过的书籍和自己的成长过程&#xff0c;目的是给想了解 JavaScript或者是刚接触JavaScrip…

【青少年编程竞赛交流】10月份微信图文索引

10月份微信图文索引 由于“组队学习”这个公众号的功能主要是组织Datawhale社群中的学习者们每个月的组队学习&#xff0c;所以&#xff0c;我另外新建了这个微信公众号“青少年编程竞赛交流”&#xff0c;在这个公众号上分享有关青少年编程方面的知识。如果大家需要就关注这个…

java 简单万年历_JAVA实现的简单万年历代码

本文实例讲述了JAVA实现的简单万年历。分享给大家供大家参考&#xff0c;具体如下&#xff1a;import java.util.Scanner;public class PrintCalendar {public static void main(String[] args) {int years 0;int month 0;int days 0;boolean isRun false;//從控制台輸入年…

mongoDB 入门指南、示例

http://www.cnblogs.com/hoojo/archive/2011/06/01/2066426.html mongoDB 入门指南、示例 上一篇&#xff1a;简单介绍mongoDB 一、准备工作 1、 下载mongoDB 下载地址&#xff1a;http://www.mongodb.org/downloads 选择合适你的版本 相关文档&#xff1a;http://www.mongodb.…

中国电子学会图形化四级编程题:成语接龙

「青少年编程竞赛交流群」已成立&#xff08;适合6至18周岁的青少年&#xff09;&#xff0c;公众号后台回复【Scratch】或【Python】&#xff0c;即可进入。如果加入了之前的社群不需要重复加入。 我们将有关编程题目的教学视频已经发布到抖音号21252972100&#xff0c;小马老…

sidecar学习

1、SideCar的出现 微服务的结构是细粒度的&#xff0c;由多个服务构成&#xff0c;支持不同的服务用不同的语言来编写&#xff0c;比如a服务用python&#xff0c;b服务用java&#xff0c;C服务用php等&#xff0c;我们称为异构语言&#xff0c;那么在利用zuul来代理访问服务的时…

java整数常量区_在Java中,我可以用二进制格式定义一个整数常量吗?

所以&#xff0c;随着Java SE 7的发布&#xff0c;二进制表示法是标准的。 如果你对二进制有一个很好的理解&#xff0c;语法是非常简单明了的。byte fourTimesThree 0b1100; byte data 0b0000110011; short number 0b111111111111111; int overflow 0b1010101010101010101…

[LeetCode 120] - 三角形(Triangle)

问题 给出一个三角形&#xff0c;找出从顶部至底部的最小路径和。每一步你只能移动到下一行的邻接数字。 例如&#xff0c;给出如下三角形&#xff1a; [ [2], [3,4], [6,5,7], [4,1,8,3] ] 从顶部至底部的最小路径和为11&#xff08;即235111&#xff09;。 注意&#xff1a; …

中国电子学会scratch等级考试四级编程题:找出出现次数最多的数字

「青少年编程竞赛交流群」已成立&#xff08;适合6至18周岁的青少年&#xff09;&#xff0c;公众号后台回复【Scratch】或【Python】&#xff0c;即可进入。如果加入了之前的社群不需要重复加入。 我们将有关编程题目的教学视频已经发布到抖音号21252972100&#xff0c;小马老…

人工智能 有信息搜索 (启发式)

一、最佳优先搜索 根据评价函数选择表现的最佳的节点进行扩展 最佳优先搜索 best-first-search 算法 不同的方法有不同的评价函数 启发函数&#xff0c;标记h(x) h(n)从节点n到目标的最低耗散估计值 启发函数是额外信息的一种最普通的形式 二、贪婪最佳优先搜索 最先扩展离目标…

java 排序算法 讲解_java实现排序算法之冒泡排序法详细讲解

冒泡排序(Bubble Sort)&#xff0c;是一种计算机科学领域的较简单的排序算法。它重复地走访过要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换&#xff0c;也就是说该数列已经排序。这…

24、线程控制

线程有一套完整的与其有关的函数库可供调用&#xff0c;它们中的绝大多数函数名都以pthread_开头。为了调用这些函数库&#xff0c;必须在程序中包含头文件pthread.h,并且在比那一程序时使用选项-lpthread来链接线程库。 1、线程标识 就像每个进程有一个进程ID一样&#xff0c;…

Datawhale组队学习周报(第038周)

本周报总结了从 11月01日至11月07日&#xff0c;Datawhale组队学习的运行情况&#xff0c;我们一直秉承“与学习者一起成长的理念”&#xff0c;希望这个活动能够让更多的学习者受益。 第 30 期组队学习一共 8 门开源课程&#xff0c;共组建了 8 个学习群&#xff0c;参与的学…

OpenGL概念辨析: 窗口,视口,裁剪区域

1.窗口&#xff1a;这就不用解释了吧 2.视口&#xff1a;就是窗口中用来显示图形的一块矩形区域&#xff0c;它可以和窗口等大&#xff0c;也可以比窗口大或者小。只有绘制在视口区域中的图形才能被显示&#xff0c;如果图形有一部分超出了视口区域&#xff0c;那么那一部分是看…

java源码推荐_基于java的推荐系统实现源代码

【实例简介】常用推荐算法java实现~涉及多种相似度计算&#xff0c;比如cosine相似度&#xff0c;欧氏距离等~(recommand algirithm )【实例截图】【核心代码】RecommendSystemJavaCode└── Recommend└── src├── collaborative│ ├── cache│ │ ├── FileS…

ref与out的区别

前一段时间老用ref与out 感觉他们的效果差不多&#xff0c;就去网上查了一下他们的区别&#xff0c;网上说的概念性的东西太多了&#xff0c;后来通过自己的摸索发现他们有一个规律 ref: 在引用方法之外必须赋初值 static void TestRefAndRef(){string s1"test";Test…

【组队学习】【31期】组队学习内容详情

第31期 Datawhale 组队学习活动马上就要开始啦&#xff01; 本次组队学习的内容为&#xff1a; IOS开发基于Python的办公自动化吃瓜教程——西瓜书南瓜书LeetCode 刷题李宏毅机器学习&#xff08;含深度学习&#xff09;动手学数据分析SQL编程语言数据可视化&#xff08;Matpl…

区块链到底是什么?

2019独角兽企业重金招聘Python工程师标准>>> 欢迎大家前往腾讯云社区&#xff0c;获取更多腾讯海量技术实践干货哦~ 翻译人&#xff1a;ArrayZoneYour&#xff0c;该成员来自云社区翻译社 原文链接&#xff1a;https://www.investinblockchain.com/what-exactly-is-…

java怎么返回xml_java – 如何从Web服务返回XML

这可能是疯狂/愚蠢/愚蠢/冗长的问题之一,因为我是网络服务的新手.我想写一个Web服务,它将以XML格式返回答案(我正在使用我的服务进行YUI自动完成).我正在使用Eclipse和Axis2并遵循http://www.softwareagility.gr/index.php?qnode/21我希望以下列格式回复代码元素的数量可能因响…

jsp路径问题

绝对路径&#xff1a;/StudentInfo/images/login.jpg 相对路径&#xff1a;images/login.jpg 路径前面的第一个/代表tomcate目录下面的webapps这个文件夹 jsp的Advanced模版。。。默认有一个基准路径,所有写的路径都会变成绝对路径。 测试的时候发现&#xff0c;在IE下面可以正…

写一篇C语言入门第一讲

嗨~大家好~ 我是小白&#xff0c;最近才使用这个博客&#xff0c;我是一个计算机系的学生&#xff0c;我会在这里发一些我给我们班其他同学讲C语言入门的博文&#xff0c;希望大家能共享这些资料&#xff0c;当然了&#xff0c;我也很希望大家给我提出好的意见或建议。&#x…

李嘉骐:03 PyTorch模块与基础实战

深入浅出Pytorch 03 PyTorch模块与基础实战 内容属性&#xff1a;深度学习&#xff08;实践&#xff09;专题航路开辟者&#xff1a;李嘉骐、牛志康、刘洋、陈安东领航员&#xff1a;叶志雄航海士&#xff1a;李嘉骐、牛志康、刘洋、陈安东开源内容&#xff1a;https://githu…

math.hypot java_Java之Math类

Java之Math类#Java的Math类封装了很多与数学有关的属性和方法,后续遇到常用也会直接在这篇博客更新。。。###public static void t2() {System.out.println(Math.E);//比任何其他值都更接近 e(即自然对数的底数)的 double 值。System.out.println(Math.PI);//比任何其他值都更接…

ruby Mixin用法

module MyNA"China"attr:nameattr:agedef set_name(name)namenameenddef get_namereturn nameenddef set_age(age)ageageend endclass Testinclude My endtTest.new t.set_name("history") p t.get_name 转载于:https://www.cnblogs.com/wangwenfei/p/ruby…