7

我明白準備好的語句是尋求針對SQL注入的保護的最終方式。但是,它們以有限的方式提供了覆蓋範圍;例如,如果我讓用戶決定操作的順序是什麼(即是ASC還是DESC?等),那麼我就不會在那裏使用準備好的語句。PHP:使用預準備語句並防範SQL注入vs轉義

據我所知,我可以將用戶輸入映射到預先定義的白名單。但是,這隻有在可以預先創建或猜測白名單時纔有可能。例如,在上面提到的情況下(ASC或DESC),可以很容易地根據可接受值的列表映射和驗證這種情況。但是,在這種情況下,SQL語句的部分無法通過白名單進行驗證嗎?

如果存在這種情況,那麼推薦的方法是什麼?

如果我要在整個開發板中使用基礎數據庫的內置轉義實用程序(例如mysql的mysqL_real_escape_string)來轉義user_input,我會在哪裏失敗?

我問與我總是建立我的SQL語句加引號的值假設這個問題 - 即使是整數...

讓我們來看看下面的例子,並在其上反映..

select {$fields} from {$table} where Age='{$age}' order by {$orderby_pref} 

假設所有變量都是用戶提供的。

如果我要mysql_real_escape_string上述SQL中的所有變量(而不是使用準備好的語句,這些語句覆蓋了我只是中途迫使我爲另一半列出白名單而無法提供幫助),難道不是嗎?同樣安全(並且更容易編碼)?如果不是,哪個輸入場景轉義實用程序會失敗?

$fields  = mysql_escape($fields); 
$table  = mysql_escape($table); 
$age   = mysql_escape($age); 
$orderby_pref = mysql_escape($orderby_pref); 

select {$fields} from {$table} where Age='{$age}' order by {$orderby_pref} 
+2

因爲你知道並關心sql注入(相對於大多數人在PHP標記中提出問題而言) – Mahn 2012-07-12 15:18:09

+0

動態SQL和將值添加到查詢之間是有區別的。綁定參數或字符串連接(加上轉義)都可以用於添加值。動態SQL需要將可接受的語法列入白名單。因此,使用正則表達式,映射或switch語句來添加「DESC」或「ORDER」和其他限定符。儘管如此,存儲過程將是一個冗長的選擇。 – mario 2012-07-12 15:25:33

回答

3

無論您使用預處理函數還是使用mysql轉義函數,您總是需要使用白名單來處理表或列名稱等內容。

問題是表名和列名不是用單引號或雙引號括起來的,所以如果你使用了一個特別引用這些字符的函數(當然還有一些......),它將不會爲你的表名。考慮表名my_table; DELETE * FROM mysql; SELECT * FROM my_table。這個字符串中的任何內容都不會被mysql的轉義函數轉義,但它肯定是一個字符串,您希望檢查一個白名單。

除此之外,mysql轉義函數有一個字符集可能導致它們無用的問題,所以你總是用預處理語句更好。

+2

'my_table; DELETE * FROM mysql; SELECT * FROM my_table'確保它已關閉,爲什麼MYSQL_ESCAPE方法會失敗....因此,在簡單易懂的談話中,SQL注入保護的唯一選擇(對於準備好的語句或數據庫的部分轉義實用程序不起作用),就是使用白名單方法。這個結論是否正確? – 2012-07-12 15:35:19

+0

@平均喬這是正確的。 – jeroen 2012-07-12 15:39:54

+0

+1用於提示白名單方法 – Mahn 2012-07-13 01:11:42

3

您可以使用PDO和你的生活將變得更容易...:

# Order 
    switch(strtoupper($Order)){ 
     default: 
     case 'ASC': 
      $Order = 'ASC'; 
      break; 

     case 'DESC': 
      $Order = 'DESC'; 
      break; 
    } 

    # ID 
    $ID = 39; 
    $Username = 'David'; 

    # Query 
    $Query = $this->DB->Main->prepare('SELECT * FROM Table WHERE ID = :ID AND Username = :Username ORDER BY HellBob '.$Order); 
    $Query->bindValue(':ID', $ID, PDO::PARAM_INT); 
    $Query->bindValue(':Username', $Username, PDO::PARAM_STR); 

    # All good ? 
    if(!$Query->execute()){ 
     exit('Error'); 
    } 

    // Results 
    $Row = $Query->fetch(PDO::FETCH_ASSOC); 

您不必擔心引號或SQL注入。你可以使用簡單的「白名單」來提高你的查詢的可變性。

+1

您忘記了訂購區域;-) – Waygood 2012-07-12 15:15:46

+0

@Waygood Oups。謝謝。更新了! – 2012-07-12 15:17:44

+1

這並沒有真正回答這個問題,因爲您在查詢中對錶名,列名和排序順序列進行了硬編碼。 – jeroen 2012-07-12 15:19:56