PHP 5.3.6及之前版本的PDO的bindParam,bindValue潛在的安全隱患 mysql
使用PDO的參數化查詢時,可使用bindParam,bindValue爲佔位符綁定相應的參數或變量, 咱們每每使用以下格式: 程序員
$statement->bindParam(1, $string); sql
$statement->bindParam(2, $int, PDO::PARAM_INT); 安全
我在之前的文章中分析介紹過PDO的ATTR_EMULATE_PREPARES屬性對PDO底層協議與MySQL Server通信機制影響:
函數
1. 默認狀況下,PDO會使用DSN中指定的字符集對輸入參數進行本地轉義(PHP手冊中稱爲native prepared statements),而後拼接成完整的SQL語句,發送給MySQL Server。這有些像咱們平時程序中拼接變量到SQL再執行查詢的形式。 spa
這種狀況下,PDO驅動可否正確轉義輸入參數,是攔截SQL注入的關鍵。然而PHP 5.3.6及老版本,並不支持在DSN中定義charset屬性(會忽略之),這時若是使用PDO的本地轉義,仍然可能致使SQL注入,解決辦法後面會提到。 server
2. MySQL Server 5.1開始支持prepare預編譯SQL機制,即SQL查詢語句與參數分離提交,但這個特性須要客戶端協議支持支持,目前全部版本的PHP均支持。
若是PDO客戶端使用mysql Server的prepare功能,大體的交互過程是:
A. PDO將SQL模板發送給mysql server, SQL模板即包含參數佔位符(問號或命名參數)的SQL語句 ci
B. PDO不對輸入參數做任何轉義處理,將參數的位置,值,類型等等信息發送給MySQL Server pdo
C. PDO客戶端調用execute statement 字符串
D. MySQL Server 對B步驟提交的參數進行內部轉義,並執行查詢,返回結果給客戶端。
看到沒有,若是使用了mysql server prepare功能,則字符串的轉義是由MySQL Server完成的。mysql會根據字符集(set names <charset>)對輸入參數轉換,確保沒有注入產生。
PDO默認狀況下使用了本地模擬prepare(並未使用MySQL server的prepare),若是要禁止PDO本地模擬行爲而使用MySQL Server的prepare機制,則須要設置PDO的參數:
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);
若是你使用了PHP 5.3.6及之前版本,強烈推薦使用上述語句增強安全性。
若是你使用PHP 5.3.6+, 則請在DSN中指定 charset,是否設置上述參數,都是安全的。
好了,如今步入正題,假設有如下代碼邏輯:
$pdo = new PDO("mysql:host=localhost;dbname=test;charset=utf8",'root','');
$pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE,PDO::FETCH_ASSOC);
$pdo->exec('set names utf8');
$id = '0 or 1 =1 order by id desc';
$sql = "select * from article where id = ?";
$statement = $pdo->prepare($sql);
$statement->bindParam(1, $id, PDO::PARAM_INT);
$statement->execute();
假設$id是外部變量(由其它函數傳遞,或用戶提交),咱們也沒有使用intval對這兩個參數進行強制類型轉換(咱們認爲使用PDO綁定參數時已經指定參數類型爲INT, 即PDO::PARAM_INT),期待PDO能使用咱們指定的類型對其進行轉義,可是事實上呢?卻有太多不肯定因素:
1. 以上代碼實測在PHP 5.2.1形成了SQL注入
2. 以上代碼在PHP 5.3.9下,沒有形成任何SQL注入
那麼,若是使用了存在bug的PHP版本,那麼如何從根本上解決這個問題?
1. 設置PDO不使用本地模擬prepare, 即 $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);
這樣,即便不使用intval對輸入進行轉換,也能夠確保是安全的。若是因爲程序員遺忘沒有使用intval轉換,那麼仍是存在安全隱患的。
使用這種方式,更完全,更安全。