2010-07-21 71 views
0

我有以下插入函數。從sql注入安全嗎?如果不是那麼我該如何安全。這是PDO函數安全從sql注入

public function insert($postValues, $table){ 

    $dbh = $this->connect(); 

    try { 
     $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 

     $fields = implode(array_keys($postValues), ','); 
     $values = "'".implode(array_values($postValues), "','")."'"; 
     $insertQuery = 'INSERT INTO '.$table.' ('.$fields.') VALUES (:'.$fields.')'; 

     $stmt = $dbh->prepare($insertQuery); 

     foreach($postValues as $vals) { 
      $stmt->execute($vals); 
     } 

     $message = $sucessMessage; 
    } 
    catch(PDOException $e){ 
     $message = $e->getMessage(); 
    } 

    $dbh = null; 

    return $message; 
} 

由於提前

回答

2

如果每個列類型爲PDO::PARAM_STR,那麼它是相當簡單的爲您的參數綁定使用PDOStatement::execute unamed放慢參數標記。但是,如果列類型有所不同,那麼當您使用PDOStatement::bindParam綁定到每列時,您需要指定每列的列類型。

從似乎是用戶輸入的表中接受表名和列名不是一個好主意。如果表名或列​​名不正確,查詢將會失敗,但您需要非常小心以確保表和列名可以安全使用。以下示例在執行任何SQL之前檢查表和列名稱與白名單:

function insert($postValues, $table) { 
    $dbh = $this->connect(); 

    // Create a simple whitelist of table and column names. 
    $whitelist = array('my_table' => array('col1', 'col2', 'col3')); 

    // Check if the table name exists in the whitelist. 
    if(!array_key_exists($table, $whitelist)) { 
     exit("$table is not a valid table name.\n"); 
    } 

    // Count the number of columns that are found in the whitelist. 
    $cols = count(
     array_intersect(
      $whitelist[$table], 
      array_keys($postValues))); 

    if($cols !== count($postValues)) { 
     exit("One or more invalid column names have been supplied.\n"); 
    } 

    // Create a comma separated list of column names. 
    $columns = implode(', ', array_keys($postValues)); 
    // Create a comma separated list of unnamed placeholders. 
    $params = implode(', ', array_fill(0, count($postValues), '?')); 
    // Create a SQL statement. 
    $sql = "INSERT INTO $table ($columns) VALUES ($params)"; 

    // Prepare the SQL statement. 
    $stmt = $dbh->prepare($sql); 
    // Bind the values to the statement, and execute it. 
    return $stmt->execute(array_values($postValues)); 
} 

echo insert(
    array(
     'col1' => 'value1', 
     'col2' => 'value2', 
     'col3' => 'value3'), 
    'my_table'); 

// 1 

echo insert(
    array(
     'col1' => 'value1', 
     'col2' => 'value2', 
     'col3' => 'value3'), 
    'unsafe_table'); 

// unsafe_table is not a valid table name. 

echo insert(
    array(
     'col1' => 'value1', 
     'col2' => 'value2', 
     'unsafe_col' => 'value3'), 
    'my_table'); 

// One or more invalid column names have been supplied. 
-1

沒有,因爲你只是執行與PDO擴展名的原始SQL查詢。我做類似於以下內容:

$fields = array(); 
$values = array(); 

foreach ($_POST as $field => $value) { 
    $fields[] = $field; 
    $values[] = $this->pdo->quote($value); 
} 

$fields = implode(',', $fields); 
$values = implode(',', $values); 

$sql = "INSERT INTO $table ($fields) VALUES ($values)"; 
$res = $this->pdo->query($sql); 

我確信您可以修改上述以適合您的設置。

+0

但是,可以從文檔(http://php.net/manual/en/pdo.quote .php),你最好使用prepare()。您可以使用佔位符輕鬆動態地創建一個sql字符串,並隨後調用bindParam/bindValue將它們綁定到該語句。 – 2010-07-21 11:46:10

+0

是的,確實如此。雖然如果我遍歷我的'$ _POST'數據(或者我使用的任何數組),我也可以對數據進行操作。例如,在查詢或鍵/值對中使用哈希密碼之前,如果我正在更新並且用戶沒有輸入任何內容作爲其新密碼,那麼哈希密碼就完全相同。如果我沒有刪除密鑰,則會清空一個空字符串並破壞用戶的登錄。只是想一想。 – 2010-07-21 11:55:04

+0

@Dennis:不幸的是,您不能輕鬆地將數組值綁定到預準備語句。所以上面的方法是可以的。當然,您可以動態構建準備好的SQL語句(根據需要添加儘可能多的佔位符),然後將每個數組項綁定到此語句。 – 2010-07-21 11:56:56

1

順便說一句:問什麼時候,如果PDO是從SQL注射比其他一些PHP MySQL連接庫更安全,答案是NO 當我們談論PDO_MYSQL(不知道下面的一些其他數據庫是真實的)。

人們甚至可以說反過來,PDO的安全性較低,比其他任何PHP MySQL連接庫(ext/mysqlext/mysqli)更危險,因爲PDO_MYSQL允許在一個SQL語句中多次查詢,同時ext/mysql完全停止多查詢和ext/mysqli有一個sparate功能mysqli_multi_query()

我只是試圖找到任何資源來支持這種說法,但我發現的唯一的事情是:

+2

也許這樣,但一旦您使用準備好的語句(如您應該),問題就不復存在。 – 2010-07-21 12:11:22

+0

@丹尼斯:這是正確的...... – 2010-07-21 12:14:28

4

的唯一明智的方式是使用與PDO::prepare參數(見手冊的例子)。此外,字段名稱應該來自可信來源,即不是用戶。這樣一來,從受信任組件構建您的查詢字符串:

function insert ($table, $fields, $data) 
{ 
    $field_names = implode (", ", $fields);      # "a, b" 
    $values = ":" . implode (", :", $fields);     # ":a, :b" 
    $query = "INSERT INTO $table($field_names) VALUES($values)"; 
    $sth = $pdo->prepare ($query); 

    foreach ($data as $row) { 
     # Here you can even remove "bad" keys from $row 
     $sth->execute ($row); 
    } 
} 

$fields = array ('a', 'b'); # those are hard-coded in application 
$data = array (   # those come from user 
    array ('a'=>'Apple', 'b'=>'Bean'), 
    array ('a'=>'Avocado', 'b'=>'Blueberry', '); DELETE FROM fruits; -- '=>'evil'), 
); 
insert ('fruits', $fields, $data); 
+0

邁克的回答在這種情況下更好。上面的語句執行的查詢數量與字段數量相同,可以爲數百個,而不是單個INSERT查詢 – JM4 2012-01-31 16:23:34