PDO防注入的核心:查询语句模板和参数是分两次提交到mysql,另外参数转义由mysql自身完成。

PDO就是一个通用PHP数据库操作类,从php5开始提供,并从6开始成为默认的数据库操作接口;

由于PDO使用预编译的形式进行操作数据库,因此只要不是底层的漏洞(php 5.3.8之前的PDO存在漏洞),可以100%抵挡注入漏洞!

PDO默认并不是安全的,必须禁用pdo的prepared statements的仿真效果

$dbh = new PDO("mysql:host=localhost; dbname=demo", "user", "pass");
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);//禁止PHP本地转义而交由MySQL Server转义才是安全的,为什么呢?看下面代码
$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;
关键在其机制:当调用 prepare() 时,查询语句已经发送给了数据库服务器,此时只有占位符 ? 发送过去,没有用户提交的数据;当调用到 execute()时,用户提交过来的值才会传送给数据库,他们是分开传送的,
两者独立的(由MySQL完成变量的转义处理),SQL攻击者没有一点机会。,但需要在DSN中指定charset属性,如:
 

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

 

但是我们需要注意的是以下几种情况,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;
EXTRACT:用于抽取时间的单独部分如天,秒等
 4.给mysql Server指定字符集,并将变量发送给MySQL Server完成根据字符转义才能防止注入,看下面例子就不能防止注入

$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));

有个问题,如果在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编码。省却了程序中的转换编码问题,不要有疑问,这样做不会产生乱码。



posted on 2014-10-16 18:09  京刚  阅读(356)  评论(0编辑  收藏  举报