2009-10-09 4 views
26

В настоящее время я создаю запрос, в котором как поле, так и столбцы и значения могут состоять из введенных пользователем данных.Экранирование имен столбцов в операторах PDO

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

  • mysql_real_escape_string требует ресурс соединения MySQL для того, чтобы нас таким образом, что исключается
  • PDO :: цитата добавляет кавычки вокруг имен полей, которое делает их бесполезными в запросах тоже
  • addslashes работает, но не действительно безопасен

У любого есть идея, что лучший способ правильно вставить имена полей в запрос, прежде чем передавать его в PDO :: prepare?

ответ

28

Стандартный способ ANSI делать разделители является:

SELECT "field1" ... 

и если есть "в имени, дважды его:

SELECT "some""thing" ... 

К сожалению, это не работает в MySQL с настройками по умолчанию, потому что MySQL предпочитает думать, что двойные кавычки являются альтернативой одинарным кавычкам для строковых литералов. В этом случае вам нужно использовать обратные образы (как описано Björn) и обратную косую черту.

Чтобы сделать обратную косую черту правильно, вы получите : нужен mysql_real_escape_string, потому что он зависит от набора символов. Но вопрос спорный, потому что ни mysql_real_escape_string, ни addlashes не избегают символа backquote. Если вы можете быть уверены, что в именах столбцов никогда не будет символов, отличных от ASCII, вы можете избежать просто ручного обратного слэша - экранирования символов `и \.

В любом случае, это несовместимо с другими базами данных. Вы можете сказать MySQL, чтобы разрешить синтаксис ANSI, установив опцию конфигурации ANSI_QUOTES. Аналогичным образом, по умолчанию SQL Server также дросселирует по двойным кавычкам; он использует еще один синтаксис, а именно квадратные скобки. Опять же, вы можете настроить его для поддержки синтаксиса ANSI с опцией «quoted_identifier».

Резюме: если вам нужно только совместимость MySQL:

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

Если вам нужно кросс-DBMS совместимость, либо:

б. используйте двойные кавычки и требуйте, чтобы пользователи MySQL/SQL-Server соответствующим образом изменили конфигурацию. Запретить двойные кавычки в имени (поскольку Oracle не может справиться с ними даже с экранированием). Или,

c. имеют настройку для MySQL vs SQL Server vs Others и создают в зависимости от этого либо синтаксис backquote, square bracket или double-quote. Запретить как двойные кавычки, так и обратную косую черту/backquote/nul.

Это то, на что вы надеетесь, что уровень доступа к данным будет иметь функцию, но PDO этого не делает.

Резюме резюме: произвольные имена столбцов являются проблемой, лучше всего избегать, если вы можете ей помочь.

Сводная информация о резюме: gnnnnnnnnnnnh.

+3

Спасибо за разъяснение дебильная ситуация. Я думаю, что подход для взрослых здесь может состоять в том, чтобы просто вызвать исключение, если имя столбца не совпадает с [a-z0-9_], а не пытается кодировать все возможные дыры. –

+0

А что насчет @Maryam Jeddian? 'str_replace (" \ '", "\' \ '", $ fieldname) 'выглядит разумно. Внутри backticks вам не нужно скрывать косые черты. Я не уверен в нулевых символах. Любые мысли об этом ответе? –

+2

@ Kamil: Хм ... Я мог бы поклясться, что, когда я последний раз проверял, обратные косые черты действительно имели ускользающий эффект в backticks. Все еще происходит что-то странное: попробуйте ввести SELECT \ 'a \\' \ 'b \' FROM sometable; и MySQL довольно запутан. Поэтому я придерживаюсь «не позволяю персонажам вообще». – bobince

0

Wierd дизайн проекта, но для вашей проблемы: объедините свои имена полей с помощью `и используйте addlashes для названия.

select `field1`, `field2` from table where `field3`=:value 
+0

Это действительно одно из решений, я думал, хотя и что-то намного проще ... Я просто описываю таблицу и фильтрую любые записи из массива, у которых есть ключи, которые не относятся к столбец в моей таблице ...таким образом, я абсолютно уверен, что используются только существующие столбцы, и мы генерируем исключение в em – ChrisR

+2

, это не сработает, потому что пользователь все еще может вырваться из backticks, указав имя поля, содержащее обратную ссылку –

13

Правильный ответ, является

str_replace("`", "``", $fieldname)

Неправильно:

 
mysql> SELECT `col\"umn` FROM user; 
ERROR 1054 (42S22): Unknown column 'col\"umn' in 'field list' 

Справа:

 
mysql> SELECT `kid``s` FROM user; 
ERROR 1054 (42S22): Unknown column 'kid`s' in 'field list' 
mysql> SELECT ```column``name``` FROM user; 
ERROR 1054 (42S22): Unknown column '`column`name`' in 'field list' 

(Обратите внимание, что в последнем примере, имя столбца имеет 3 (три) дополнительных назад- тикает в нем, просто чтобы показать крайний случай)

2

Это может повлиять на производительность, b Это должно быть безопасно.

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

Если есть соответствие, то вы можете использовать предоставленные пользователем данные без необходимости каких-либо экранов.

Если нет совпадений, это опечатка или взлома - в любом случае это «ошибка» в введенных данных, и запрос не должен запускаться.

То же самое можно было бы сделать для имен динамических таблиц, запустив запрос SHOW TABLES и соответствующий результат.

В одном из моих приложений у меня есть сценарий «install»; часть этого запрашивает имена баз данных и полей таблицы, а затем записывает файл PHP, который всегда называют обратно, так что я не постоянно работает ОПИСАТЬ запросов Agains базы данных, например

$db_allowed_names['tableName1']['id'] = 1; 
$db_allowed_names['tableName1']['field1'] = 1; 
$db_allowed_names['tableName1']['field2'] = 1; 
$db_allowed_names['tableName2']['id'] = 1; 
$db_allowed_names['tableName2']['field1'] = 1; 
$db_allowed_names['tableName2']['field2'] = 1; 
$db_allowed_names['tableName2']['field3'] = 1; 

if($db_allowed_names['tableName1'][$_POST['field']]) { 
    //ok 
} 

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

1

Как насчет этого?

function filter_identifier($str, $extra='') { 
    return preg_replace('/[^a-zA-Z0-9_'.$extra.']/', '', $str); 
} 

try { 
    $res = $db->query('SELECT '.filter_identifier($_GET['column'], '\*').' FROM '.filter_identifier($_GET['table']).' WHERE id = ?', $id); 
} catch (PDOException $e) { 
    die('error querying database'); 
} 

Это простой список символов на основе белого списка. Любые символы, не входящие в список, будут удалены. К счастью для меня, мне удалось создать базу данных и таблицы, поэтому я знаю, что никогда не будет никаких символов вне «a-zA-Z0-9_» (обратите внимание: нет места). Вы можете добавить дополнительные символы в список через $ extra arg. Если кто-то попытаться положить "(SELECT * FROM пользователей); -" в 'колонке', он будет фильтровать вниз к "SELECT * FROMusers", который бы бросил исключение :)

I старайтесь избегать дополнительных запросов, если это вообще возможно (я очень чувствителен к производительности). Так что, например, делать DESCRIBE заранее или жестко кодировать массив таблиц/столбцов для проверки, я бы предпочел не делать.

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