2010-07-21 2 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, то это довольно просто, чтобы связать ваши параметры unamed маркеров с помощью параметра Я PDOStatement::execute. Однако, если типы столбцов меняются, тогда вам нужно указать тип столбца для каждого столбца при привязке к нему с помощью 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

Нет, потому что вы просто исполняющие необработанного SQL запрос с расширением PDO. Я делаю что-то похожее на следующее:

$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, чтобы связать их с инструкцией. –

+0

Да, действительно. Хотя, если я перебираю свои данные '$ _POST' (или любой другой массив, который я использую), я также могу действовать на данные. Например, хэш-пароли до того, как они будут использованы в запросе или пара ключ/значение полностью, если я обновляюсь, и пользователь ничего не вводил в качестве своего нового пароля. Если бы я не удалял ключ, пустая строка была бы хэширована и нарушала бы логин пользователя. Только один сценарий, о котором нужно подумать. –

+0

@Dennis: К сожалению, вы не можете легко привязать значения массива к подготовленным операторам. Таким образом, подход выше - это нормально. Конечно, вы можете динамически строить подготовленный оператор SQL (добавляя столько заполнителей, сколько вам нужно), а затем привязывать каждый элемент массива к этому утверждению. –

1

Кстати: когда просят, если PDO безопаснее от SQL инъекции, чем какой-либо другой PHP MySQL библиотеки подключений, ответ НЕТ, когда мы говорим о PDO_MYSQL (не знаю, если верно следующее для некоторых других баз данных).

Можно даже утверждать, наоборот, PDO менее безопасным и более опасным, чем любой другой библиотеке подключений PHP MySQL (ext/mysql и ext/mysqli), потому что PDO_MYSQL позволяет несколько запросов в одном SQL заявления, а ext/mysql останавливает мульти-запросы полностью и ext/mysqli имеет раздельную функцию mysqli_multi_query().

Я просто пытался найти какие-либо источники, чтобы поддержать это заявление, но единственное, что я нашел являются:

+2

Возможно, так, но как только вы используете подготовленные заявления (как и должны), эта проблема перестает существовать. –

+0

@ Dennis: Это правильно ... –

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

Смежные вопросы