В настоящее время я создаю запрос, в котором как поля, так и столбцы и значения, возможно, состоят из введенных пользователем данных.
Проблема заключается в том, чтобы избежать имен полей. Я использую подготовленные операторы, чтобы правильно избежать и процитировать значения, но при выходе из имен полей я столкнулся с трудностями.
Кто-нибудь имеет представление о том, как лучше всего правильно вставить имена полей в запрос, прежде чем передавать его в PDO :: prepare?
Стандартным способом 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.
Правильный ответ:
str_replace ("` "," `` ", $ fieldname)
Неправильно:
mysql> SELECT `col \" umn` FROM user; ОШИБКА 1054 (42S22): Неизвестный столбец 'col \ "umn' в 'списке полей'
Правильно:
mysql> SELECT `kid``s` FROM user; ОШИБКА 1054 (42S22): Неизвестная колонка «kid`s» в «списке полей» mysql> SELECT `` `column``name``` FROM user; ОШИБКА 1054 (42S22): Неизвестный столбец `` column`name` 'в' списке полей '
(Обратите внимание, что в последнем примере имя столбца содержит 3 (три) дополнительных обратных тика в нем, чтобы показать крайний случай)
Это может повлиять на производительность, но оно должно быть безопасным.
Сначала запустите запрос таблицы 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
Как насчет чего-то подобного?
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 заранее или жестко кодировать массив таблиц / столбцов для проверки, я бы предпочел не делать.
Уверенный дизайн проекта, но для вашей проблемы: объедините свои имена полей с помощью `и используйте addlashes для имени.
select `field1`, `field2` from table where `field3`=:value