Безопасное использование подготовленных операторов для запроса базы данных - PullRequest
5 голосов
/ 05 февраля 2010

Я пытаюсь написать функцию, которая универсальна в запросах, которые ей разрешено делать, но также безопасна для инъекций . Приведенный ниже код выдает ошибку как есть, но если я запускаю ее с «name» вместо «: field», она работает нормально.

$field = "name";
$value = "joe";

function selectquery($field, $value)
  {
  global $dbcon;

  $select = $dbcon->prepare('SELECT * FROM tester1 WHERE :field = :value');
  if($select->execute(array(':field' => $field, ':value' => $value)));
    {
    $row = $select->fetch();
    for ($i=0; $i<3; $i++)
      {
      echo $row[$i]."\n";
      }
    }  
  }

Как бы я позволил изменять таблицу / поля / значения, не допуская атак с использованием инъекций? mysql_real_escape_string () выглядит как шаг назад. Есть идеи?

Ответы [ 6 ]

3 голосов
/ 05 февраля 2010

Я могу ошибаться, но я не верю, что вы можете указывать поля в качестве параметров в PDO.

Почему бы просто не указать его в качестве аргумента функции? В отличие от данных, предоставляемых пользователем, поля конечны, хорошо определены и не меняются часто. Как в

selectquery('name',$value);

и ваш запрос будет

$field = "name";
$value = "joe";

function selectquery($field, $value)
  {
  global $dbcon;

  $select=$dbcon->prepare("SELECT * FROM tester1 WHERE $field = :value");
  if($select->execute(array(':value' => $value)));
 //etcetera  

Поскольку вы сами вводите имя поля для вызова функции, это безопасно, если вы не беспокоитесь, что собираетесь атаковать себя с помощью SQL-инъекции.

Если по какой-то странной причине имя поля исходит от пользовательского ввода, вы можете создать массив разрешенных полей. Таким образом, вы защищены от внедрения, потому что значения могут поступать только из вашего массива. Я не знаю, почему имя поля берется из пользовательского ввода и поэтому не заслуживает доверия, разве что вы создаете API? В противном случае, вероятно, есть лучший способ достичь цели.

Во всяком случае, это было бы потенциальное решение, использовать белый список для имен таблиц:

$field = "name";
$value = "joe";

$allowed_fields=array('name','other_name','sandwich');

function selectquery($field_name, $value)
  {
  global $dbcon,$allowed_fields;

  if(!in_array($field_name,$allowed_fields)){ return false; }
  else{ $field=$field_name; }

  $select=$dbcon->prepare("SELECT * FROM tester1 WHERE $field = :value");
  if($select->execute(array(':value' => $value)));
  //etcetera
1 голос
/ 05 февраля 2010

Использовать MDB2 autoExecute
http://pear.php.net/manual/en/package.database.mdb2.intro-auto.php

<?php
// Once you have a valid MDB2 object named $mdb2...
$table_name = 'user';

$fields_values = array(
    'id'      => 1,
    'name'    => 'Fabien',
    'country' => 'France'
);
$types = array('integer', 'text', 'text');

$mdb2->loadModule('Extended');
$affectedRows = $mdb2->extended->autoExecute($table_name, $fields_values,
                        MDB2_AUTOQUERY_INSERT, null, $types);

if (PEAR::isError($affectedRows)) {
    die($affectedRows->getMessage());
}
?>
0 голосов
/ 05 февраля 2010

Повтор ответа Эндрю Мура: единственный способ - это цитирование идентификатора, а PDO не предоставляет необходимый метод. Вместо того, чтобы использовать MDB2, вы можете просто позаимствовать его реализацию цитирования идентификаторов. Функция достаточно проста, чтобы вы могли написать свою собственную и довольно легко проверить ее на наличие ошибок.

  1. Разбить входную строку на . на список частей (может быть только одна)

  2. Для каждой части:

    1. Заменить все ` на ``.
    2. Добавьте ` в начало и в конец, если деталь не пуста. *
  3. Соедините детали с помощью ..

Например, quote_identifier("one two.three") должно быть `one two`.`three` - довольно просто.

Для дополнительной безопасности вы также можете убедиться, что строка не содержит никаких символов, которые являются недопустимыми даже в указанных в кавычках идентификаторах (в частности, в значениях null, см. документы MySQL ), но на самом деле MySQL должен их перехватывать. MDB2 не беспокоит.

*: эта проверка необходима, поскольку .columnname является допустимым и должно указывать в .`columnname`, а не ``.`columnname`.

0 голосов
/ 05 февраля 2010

PHP Data Objects, к сожалению, не предоставляет метод для цитирования идентификатора поля.

В качестве альтернативы, PEAR :: MDB2 (духовный предшественник PHP Data Objects) имеет опцию ->quoteIdentifier(), которая позволяет вам достичь того, что вы хотите, безопасным способом.

function selectquery($field, $value)
  {
  global $dbcon;

  $select = $dbcon->prepare('SELECT * FROM tester1 WHERE ' . $dbcon->quoteIdentifier($field) . ' = :value');
  if($select->execute(array('field' => $field, 'value' => $value)));
    {
    $row = $select->fetchRow();
    for ($i=0; $i<3; $i++)
      {
      echo $row[$i]."\n";
      }
    }  
  }

Я понимаю, что это решение не является оптимальным (изменение уровня абстракции в середине разработки проекта громоздко), но, к сожалению, PDO не предоставляет безопасного способа сделать то, что вы хотите сделать.

0 голосов
/ 05 февраля 2010

Привязка переменной связывает ее как данные, в частности, предотвращает изменение синтаксиса запроса. Кроме того, наличие фиксированного синтаксиса позволяет механизмам анализировать подготовленные запросы один раз и затем быстро выполнять их для каждого набора значений. Я бы посоветовал вам не создавать слой для удержания рук поверх SQL, но если вам необходимо, рассмотрите preg_replace ('/ \ W /', '', $ field).

0 голосов
/ 05 февраля 2010

Идентификаторы базы данных (имена столбцов, имена таблиц и имена баз данных) не могут и не должны быть экранированы, поэтому их нельзя использовать в подготовленных SQL-запросах.

Иногда вам может потребоваться обратная связь этих идентификаторов (используйте ` для MySQL и " для SQLite).

...