再論php 5.3.6之前版本中的PDO SQL注入漏洞問題

我曾經寫一篇《PDO防注入原理分析以及使用PDO的注意事項》,裏面描述到php 5.3.6以前的PDO可能存在SQL注入之問題,並給出了完全的解決方案,有的朋友給我發電子郵件,對此有疑問,說是在php 5.3.6以前版本中未發現這個漏洞。事實上這個漏洞是存在的,本文再次給出詳細的演示代碼。 php

在php 5.3.6之前版本,運行如下代碼,便可發現,存在PDO SQL注入問題(可向info表中填充一些數據):<?php
$pdo = new PDO("mysql:host=127.0.0.1;dbname=test;charset=gbk","root");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->query('SET NAMES GBK');
$var = urldecode('%bf%27%20OR%20username%3Dusername%20%23');
$query = "SELECT * FROM info WHERE username = ?";
$stmt = $pdo->prepare($query);
$stmt->execute(array($var)); 
$r = $stmt->fetch();
print_r($r); mysql

而在php 5.3.6以上版本中,以上代碼不存在SQL注入之問題。那麼這個問題是如何產生的?如何完全防止? sql

PDO支持的prepare其實有兩種方式:
A.  PDO驅動本地轉義 稱爲 native prepare 或 emulate prepare, PDO驅動將綁定的變量進行轉義,再插入到SQL的佔位符中,造成一個完整的SQL語句交給mysql server運行, 那麼pdo用何字符集轉義變量?php 5.3.6以上版本會使用DSN中的charset屬性進行轉義,但 php 5.3.6之前版本不支持charset選項,一概使用ascii(或latin)進行轉義。

顯然,在不一樣的php 版本中,特別是綁定變量爲多字符集字符,native prepare 或 emulate prepare的行爲是有差別的。

B.  利用mysql server進行轉義, PDO將包含有參數佔位符(問號或命名參數)的SQL發送給mysql server, 請求mysql server對SQL模板進行prepare, 而後PDO再將每一個參數佔位符對應的變量發送給mysql server, 由mysql server完成轉義處理。咱們鏈接mysql通常要執行set names charset, 那麼mysql server將使用這個charset進行轉義。顯然,由於mysql server是支持多種字符集的,則不存在這個差別

意思是在 php 5.3.6之前版本中,並不支持DSN中的charset選項,全部綁定的變量均是以ascii字節進行轉義的(與字符集無關) 安全

mysql官方手冊上的描述:
http://www.php.net/manual/zh/ref.pdo-mysql.connection.php

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

如下兩種方式任選其一可解決這個問題:
A. 經過添加(php 5.3.6之前版本):$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
B.  升級到php 5.3.6 (不用設置PDO::ATTR_EMULATE_PREPARES也可)

爲了程序移植性和統一安全性,建議使用$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false)方法 fetch

相關文章
相關標籤/搜索