Экранирование имен столбцов в операторах PDO - PullRequest
26 голосов
/ 09 октября 2009

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

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

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

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

Ответы [ 5 ]

28 голосов
/ 09 октября 2009

Стандартный способ ANSI для создания идентификатора с разделителями:

SELECT "field1" ...

и если в имени есть ", удвойте его:

SELECT "some""thing" ...

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

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

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

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

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

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

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

с. иметь настройку для MySQL против SQL Server против других, и в зависимости от этого вывести синтаксис обратной кавычки, квадратной скобки или двойной кавычки. Запретить как двойные кавычки, так и обратную косую черту / обратную цитату / нуль.

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

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

Краткое содержание резюме: gnnnnnnnnnnnh.

13 голосов
/ 31 марта 2011

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

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 голосов
/ 26 мая 2011

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

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

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

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

То же самое можно сделать для «динамических» имен таблиц, запустив запрос SHOW TABLES и сопоставив его с этим набором результатов.

В одном из моих приложений у меня есть скрипт 'install'; часть этого запрашивает имена полей базы данных и таблицы, а затем записывает php-файл, на который всегда ссылаются, поэтому я не постоянно выполняю DESCRIBE запросы к базе данных, например

$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 голос
/ 31 августа 2016

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

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 users); -" в 'column', он отфильтровал бы до "SELECT * FROMusers" , что привело бы к исключение:)

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

0 голосов
/ 09 октября 2009

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

select `field1`, `field2` from table where `field3`=:value
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...