Подтвердить что ты не робот

Идентификация имен столбцов в операторах PDO

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

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

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

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

4b9b3361

Ответ 1

Стандартным способом 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:

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

Если вам нужна совместимость между СУБД, выполните следующие действия:

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

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

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

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

Резюме сводки сводки: gnnnnnnnnnnnh.

Ответ 2

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

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 (три) дополнительных обратных тика в нем, чтобы показать крайний случай)

Ответ 3

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

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

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

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

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

В одном из моих приложений у меня есть "install" script; эта часть запрашивает имена полей базы данных и таблицы, а затем записывает файл 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

Ответ 4

Как насчет чего-то подобного?

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

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

Ответ 5

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

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