Php中用PDO查询Mysql来避免SQL注入风险的方法


Posted in PHP onApril 25, 2013

当我们使用传统的 mysql_connect 、mysql_query方法来连接查询数据库时,如果过滤不严,就有SQL注入风险,导致网站被攻击,失去控制。虽然可以用mysql_real_escape_string()函数过滤用户提交的值,但是也有缺陷。而使用PHP的PDO扩展的 prepare 方法,就可以避免sql injection 风险。

PDO(PHP Data Object) 是PHP5新加入的一个重大功能,因为在PHP 5以前的php4/php3都是一堆的数据库扩展来跟各个数据库的连接和处理,如 php_mysql.dll。 PHP6中也将默认使用PDO的方式连接,mysql扩展将被作为辅助 。官方:http://php.net/manual/en/book.pdo.php

1、PDO配置
使用PDO扩展之前,先要启用这个扩展,PHP.ini中,去掉"extension=php_pdo.dll"前面的";"号,若要连接数据库,还需要去掉与PDO相关的数据库扩展前面的";"号(一般用的是php_pdo_mysql.dll),然后重启Apache服务器即可。

extension=php_pdo.dll
extension=php_pdo_mysql.dll

2、PDO连接mysql数据库
$dbh = new PDO("mysql:host=localhost;dbname=db_demo","root","password");

默认不是长连接,若要使用数据库长连接,需要在最后加如下参数:
$dbh = new PDO("mysql:host=localhost;dbname=db_demo","root","password","array(PDO::ATTR_PERSISTENT => true)");
$dbh = null; //(释放)

3、PDO设置属性

1) PDO有三种错误处理方式:

• PDO::ERrmODE_SILENT不显示错误信息,只设置错误码
• PDO::ERrmODE_WARNING显示警告错
• PDO::ERrmODE_EXCEPTION抛出异常

可通过以下语句来设置错误处理方式为抛出异常

$db->setAttribute(PDO::ATTR_ERrmODE, PDO::ERrmODE_EXCEPTION);

当设置为PDO::ERrmODE_SILENT时可以通过调用errorCode() 或errorInfo()来获得错误信息,当然其他情况下也可以。

2) 因为不同数据库对返回的字段名称大小写处理不同,所以PDO提供了PDO::ATTR_CASE设置项(包括PDO::CASE_LOWER,PDO::CASE_NATURAL,PDO::CASE_UPPER),来确定返回的字段名称的大小写。

3) 通过设置PDO::ATTR_ORACLE_NULLS类型(包括PDO::NULL_NATURAL,PDO::NULL_EmpTY_STRING,PDO::NULL_TO_STRING)来指定数据库返回的NULL值在php中对应的数值。

4、PDO常用方法及其应用
PDO::query() 主要是用于有记录结果返回的操作,特别是SELECT操作
PDO::exec() 主要是针对没有结果集合返回的操作,如INSERT、UPDATE等操作
PDO::prepare() 主要是预处理操作,需要通过$rs->execute()来执行预处理里面的SQL语句,这个方法可以绑定参数,功能比较强大(防止sql注入就靠这个)
PDO::lastInsertId() 返回上次插入操作,主键列类型是自增的最后的自增ID
PDOStatement::fetch() 是用来获取一条记录
PDOStatement::fetchAll() 是获取所有记录集到一个集合
PDOStatement::fetchColumn() 是获取结果指定第一条记录的某个字段,缺省是第一个字段
PDOStatement::rowCount() :主要是用于PDO::query()和PDO::prepare()进行DELETE、INSERT、UPDATE操作影响的结果集,对PDO::exec()方法和SELECT操作无效。

5、PDO操作MYSQL数据库实例

<?php
$pdo = new PDO("mysql:host=localhost;dbname=db_demo","root","");
if($pdo -> exec("insert into db_demo(name,content) values('title','content')")){
echo "插入成功!";
echo $pdo -> lastinsertid();
}
?>

<?php
$pdo = new PDO("mysql:host=localhost;dbname=db_demo","root","");
$rs = $pdo -> query("select * from test");
$rs->setFetchMode(PDO::FETCH_ASSOC); //关联数组形式
//$rs->setFetchMode(PDO::FETCH_NUM); //数字索引数组形式
while($row = $rs -> fetch()){
print_r($row);
}
?>

<?php
foreach( $db->query( "SELECT * FROM feeds" ) as $row )
{
    print_r( $row );
}
?>

统计有多少行数据
$sql="select count(*) from test";
$num = $dbh->query($sql)->fetchColumn();

prepare方式
$stmt = $dbh->prepare("select * from test");
if ($stmt->execute()) {
 while ($row = $stmt->fetch()) {
     print_r($row);
 }
}

Prepare参数化查询
$stmt = $dbh->prepare("select * from test where name = ?");
if ($stmt->execute(array("david"))) {
 while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
     print_r($row);
 }
}

【下面来说说重点了,如何防止 sql注入】

使用PDO访问MySQL数据库时,真正的real prepared statements 默认情况下是不使用的。为了解决这个问题,你必须禁用 prepared statements的仿真效果。下面是使用PDO创建链接的例子:

$dbh = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'pass');
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

setAttribute()这一行是强制性的,它会告诉 PDO 禁用模拟预处理语句,并使用 real parepared statements 。这可以确保SQL语句和相应的值在传递到mysql服务器之前是不会被PHP解析的(禁止了所有可能的恶意SQL注入攻击)。虽然你可以配置文件中设置字符集的属性(charset=utf8),但是需要格外注意的是,老版本的 PHP( < 5.3.6)在DSN中是忽略字符参数的。

我们来看一段完整的代码使用实例:

$dbh = new PDO("mysql:host=localhost; dbname=demo", "user", "pass");
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); //禁用prepared statements的仿真效果
$dbh->exec("set names 'utf8'");
$sql="select * from test where name = ? and password = ?";
$stmt = $dbh->prepare($sql);
$exeres = $stmt->execute(array($testname, $pass));
if ($exeres) {
 while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
     print_r($row);
 }
}
$dbh = null;

上面这段代码就可以防范sql注入。为什么呢?

当调用 prepare() 时,查询语句已经发送给了数据库服务器,此时只有占位符 ? 发送过去,没有用户提交的数据;当调用到 execute()时,用户提交过来的值才会传送给数据库,他们是分开传送的,两者独立的,SQL攻击者没有一点机会。

但是我们需要注意的是以下几种情况,PDO并不能帮助你防范SQL注入

1、你不能让占位符 ? 代替一组值,如:

SELECT * FROM blog WHERE userid IN ( ? );

2、你不能让占位符代替数据表名或列名,如:
SELECT * FROM blog ORDER BY ?;

3、你不能让占位符 ? 代替任何其他SQL语法,如:
SELECT EXTRACT( ? FROM datetime_column) AS variable_datetime_element FROM blog;

PHP 相关文章推荐
把从SQL中取出的数据转化成XMl格式
Oct 09 PHP
php的curl实现get和post的代码
Aug 23 PHP
PHP 中检查或过滤IP地址的实现代码
Nov 27 PHP
php导出excel格式数据问题
Mar 11 PHP
PDO防注入原理分析以及使用PDO的注意事项总结
Oct 23 PHP
php根据日期或时间戳获取星座信息和生肖等信息
Oct 20 PHP
PHP模糊查询的实现方法(推荐)
Sep 06 PHP
PHP中call_user_func_array回调函数的用法示例
Nov 26 PHP
在PHP 7下安装Swoole与Yar,Yaf的方法教程
Jun 02 PHP
搭建自己的PHP MVC框架详解
Aug 16 PHP
php常用字符串查找函数strstr()与strpos()实例分析
Jun 21 PHP
Laravel5.1 框架Request请求操作常见用法实例分析
Jan 04 PHP
php中防止SQL注入的最佳解决方法
Apr 25 #PHP
Apache下禁止php文件被直接访问的解决方案
Apr 25 #PHP
PHP笔记之:日期函数的使用介绍
Apr 24 #PHP
php笔记之:AOP的应用
Apr 24 #PHP
php class中self,parent,this的区别以及实例介绍
Apr 24 #PHP
PHP中::、-&amp;gt;、self、$this几种操作符的区别介绍
Apr 24 #PHP
php判断终端是手机还是电脑访问网站的思路及代码
Apr 24 #PHP
You might like
[原创]ThinkPHP让../Public在模板不解析(直接输出)的方法
2015/10/09 PHP
php封装的连接Mysql类及用法分析
2015/12/10 PHP
crontab无法执行php的解决方法
2016/01/25 PHP
PHP实现的猴王算法(猴子选大王)示例
2018/04/30 PHP
使用prototype.js 的时候应该特别注意的几个问题.
2007/04/12 Javascript
原生js拖拽(第一课 未兼容)拖拽思路
2013/03/29 Javascript
Jquery Validate 正则表达式实用验证代码大全
2013/08/23 Javascript
javascript新建标签,判断键盘输入,以及判断焦点(示例代码)
2013/11/25 Javascript
浅析document.ready和window.onload的区别讲解
2013/12/18 Javascript
实例讲解jQuery中对事件的命名空间的运用
2016/05/24 Javascript
JavaScript面试题(指针、帽子和女朋友)
2016/11/23 Javascript
微信小程序中实现一对多发消息详解及实例代码
2017/02/14 Javascript
详解如何使用Vue2做服务端渲染
2017/03/29 Javascript
node文件批量重命名的方法示例
2017/10/23 Javascript
three.js实现3D视野缩放效果
2017/11/16 Javascript
JS实现全屏预览F11功能的示例代码
2018/07/23 Javascript
Vue监听一个数组id是否与另一个数组id相同的方法
2018/09/26 Javascript
Vue.js中的extend绑定节点并显示的方法
2019/06/20 Javascript
javascript头像上传代码实例
2019/09/28 Javascript
在Python的Tornado框架中实现简单的在线代理的教程
2015/05/02 Python
Python中的with语句与上下文管理器学习总结
2016/06/28 Python
详解MySQL数据类型int(M)中M的含义
2016/11/20 Python
tensorflow 1.0用CNN进行图像分类
2018/04/15 Python
Python插入Elasticsearch操作方法解析
2020/01/19 Python
Pycharm中import torch报错的快速解决方法
2020/03/05 Python
python通过函数名调用函数的几种场景
2020/09/23 Python
Python 爬虫批量爬取网页图片保存到本地的实现代码
2020/12/24 Python
websocket+sockjs+stompjs详解及实例代码
2018/11/30 HTML / CSS
域名注册、建站工具、网页主机、SSL证书:Dynadot
2017/01/06 全球购物
TripAdvisor土耳其网站:全球知名旅行社区,真实旅客评论
2017/04/17 全球购物
保加利亚运动鞋购物网站:SneakerStudio.bg
2020/12/23 全球购物
如何估计一张表的大小(假设该表中有1万条数据)
2016/03/27 面试题
党员批评与自我批评总结
2014/10/15 职场文书
2014年环卫工作总结
2014/11/22 职场文书
初中教务主任竞聘演讲稿(范文)
2019/08/20 职场文书
vue项目多环境配置(.env)的实现
2021/07/21 Vue.js