PHP 5.3.6及之前版本的PDO的bindParam,bindValue潛在的安全隱患

PHP 5.3.6及之前版本的PDObindParam,bindValue潛在的安全隱患 mysql

 

使用PDO的參數化查詢時,可使用bindParam,bindValue爲佔位符綁定相應的參數或變量, 咱們每每使用以下格式 程序員

$statement->bindParam(1, $string); sql

$statement->bindParam(2, $int, PDO::PARAM_INT); 安全

 

我在之前的文章中分析介紹過PDOATTR_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 Serverprepare功能,大體的交互過程是:
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 Serverprepare機制,則須要設置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注入

那麼,若是使用了存在bugPHP版本,那麼如何從根本上解決這個問題?

1. 設置PDO不使用本地模擬prepare, 即 $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);

 

這樣,即便不使用intval對輸入進行轉換,也能夠確保是安全的。若是因爲程序員遺忘沒有使用intval轉換,那麼仍是存在安全隱患的。

 

使用這種方式,更完全,更安全。

相關文章
相關標籤/搜索