Как вы используете cfqueryparam в предложении ORDER BY?

Я пытаюсь быть хорошим веб-разработчиком CF и использую <cfqueryparam> для всех элементов FORM или URL, которые попадают в мои SQL-запросы.

В данном случае я пытаюсь разрешить пользователю динамически управлять предложением ORDER BY.

<cfquery datasource="MyDSN" name="qIncidents">
  SELECT IncidentID, AnimalID, IntakeDate, DxDate, OutcomeDate
  FROM Incidents
  WHERE ShelterID = <cfqueryparam cfsqltype="cf_sql_integer" value="#Arguments.ShelterID#">
  ORDER BY <cfqueryparam cfsqltype="cf_sql_varchar" value="#SortBy#">
</cfquery>

Когда я это делаю, я получаю следующую ошибку:

Элемент SELECT, обозначенный номером ORDER BY 1, содержит переменную как часть выражения, определяющего позицию столбца. Переменные разрешены только при упорядочении по выражению, ссылающемуся на имя столбца.

Любые предложения о том, как сделать это безопасно?


person Chris Brandt    schedule 20.05.2009    source источник
comment
Какую базу данных вы используете? Я могу использовать cfqueryparam с MySQL 5   -  person Sam Farmer    schedule 20.05.2009


Ответы (5)


К сожалению, вы не можете использовать CFQUERYPARAM непосредственно в предложении Order By.

Если вы хотите использовать Order By динамически, но при этом делать это безопасно, вы можете настроить CFSWITCH или аналогичную структуру для изменения вашей переменной SortBy в зависимости от некоторого условия (например, переменной URL). Как всегда, не передавайте никакие значения напрямую от пользователя, просто посмотрите на ввод пользователя и выберите из предопределенного списка возможных значений на его основе. Затем просто используйте стандартный синтаксис:

ORDER BY #SortBy#
person CloudyMusic    schedule 20.05.2009
comment
Хороший способ подумать о том, для чего вы можете и не можете использовать cfqueryparam, — это думать об этом с точки зрения данных, которые находятся в базе данных. Переменная sortBy находится вместо столбца, а не данных в столбце. Вы также не могли использовать параметр запроса для параметризации таблицы или списка столбцов. - person Ryan Guill; 20.05.2009
comment
Какую базу данных вы используете? Я могу использовать cfqueryparam в предложении заказа с MySQL. - person Sam Farmer; 20.05.2009

Я просто расширю ответ Аарона. Одна из вещей, которые я делаю, — это использование listfindnocase(), чтобы убедиться, что аргументы, переданные в предложение order by, действительны:

<cfset variables.safeSortColumn = "name">
<cfset variables.safeSortOrder = "desc">

<cfparam name="url.sortcolumn" type="string" default="#variables.safeSortColumn#">
<cfparam name="url.sortorder" type="string" default="#variables.safeSortOrder#">

<cfif listfindnocase("name,age,address", url.sortcolumn)>
    <cfset variables.safeSortColumn = url.sortcolumn>
</cfif>

<cfif listfindnocase("desc,asc", url.sortorder)>
    <cfset variables.safeSortOrder = url.sortorder>
</cfif>

<cfquery>
select *
from mytable
order by #variables.safeSortcolumn# #variables.safeSortorder#
</cfquery>
person rip747    schedule 21.05.2009
comment
Я согласен. Это хороший подход. Я немного улучшил ваш код, добавив явно безопасные переменные, которые никогда не могут содержать указанные пользователем значения. Я не думал, что это стоит отдельного ответа, и было легче показать вам, чем пытаться объяснить в комментарии. Если вам не нравится то, что я сделал, пожалуйста, откатите его назад. - person Patrick McElhaney; 21.05.2009

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

мне нравится идея использования числа в переменных запроса (url, form), чтобы указать, по какому столбцу сортировать, а затем использовать его в переключателе и преобразовать его в фактическое имя столбца, чтобы вы не подвергали имена столбцов влиянию пользователь.

что касается того, когда и зачем использовать cfqueryparam, имейте в виду, что это НЕ просто проверка ввода и предотвращение внедрения SQL (хотя это очень приятный бонус) - с cfqueryparam базовый SQL в базу данных отправляется обратно через драйвер с использованием привязки SQL переменные - значения-заполнители, поэтому оптимизатор базы данных может определить, какой индекс использовать в более общем формате... поэтому, когда вы отправляете оператор SQL, подобный этому: SELECT * FROM product WHERE ID=1 и SELECT * FROM product WHERE ID=2 оптимизатор работает оба раза. но с переменными связывания SQL выглядит следующим образом: SELECT * FROM product WHERE ID=? (?=1) и SELECT * FROM product WHERE ID=? (?=2), поэтому оптимизатор может использовать кэшированные результаты первого анализа, чтобы точно знать, какой индекс использовать во втором запросе. в зависимости от сложности SQL и базы данных это может дать ОГРОМНУЮ экономию времени. по моему опыту, это очень полезно с точки зрения производительности с столбцами oracle и date/time в предложении where.

так что, где использовать cfqueryparam, там можно использовать переменную привязки SQL...

хт джон

person Community    schedule 21.05.2009

Относительно комментария об использовании «cfqueryparam в предложении заказа в порядке с MySQL». Да, я считаю, что это разрешено с источниками данных MySQL. Хотя используется столбец порядковый номер, а не имя столбца (которое вместо этого рассматривается как постоянная строка).

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

<!--- this works --->
<cfset url.sortColumnNumber = "3">
<cfquery name="getDataByPosition" datasource="MySQLDSN">
   SELECT  RecordID, ProductName, DateAdded
   FROM TestTable
   ORDER BY <cfqueryparam value="#url.sortColumnNumber#" cfsqltype="cf_sql_integer"> ASC
</cfquery>
<cfdump var="#getDataByPosition#">

<!--- this does NOT work --->
<cfset url.sortColumnName = "DateAdded">
<cfquery name="getDataByName" datasource="MySQLDSN">
   SELECT  RecordID, ProductName, DateAdded
   FROM    TestTable
   ORDER BY <cfqueryparam value="DateAdded" cfsqltype="cf_sql_varchar"> ASC
</cfquery>
<cfdump var="#getDataByName#">

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

Да, я согласен, что защита от sql-инъекций не является основной целью cfqueryparam. Так что описание переменных связывания было хорошим дополнением.

person Leigh    schedule 21.05.2009
comment
Насколько я вижу, в ответе нет ничего неточного. Анонимный проголосовавший хочет оставить комментарий? - person Leigh; 06.05.2012
comment
Возможно, через ORDER BY 3 возникло замешательство. Если это работает на MySQL, и вы заметили, что это работает на MySQL, это элегантное решение проблемы. - person James A Mohler; 09.01.2013
comment
На самом деле, используя драйверы базы данных по умолчанию, он работает с MySQL, но не с MS SQL. Когда дело доходит до использования оридиналов, люди, как правило, либо любят его, либо ненавидят. У меня смешанные чувства. Это более элегантно, чем альтернативы, но не всегда так интуитивно понятно. Оглядываясь назад, я подозреваю, что опубликовал это, чтобы опровергнуть несколько общих заявлений о том, что это никогда не возможно, что не совсем так. Это зависит от водителя. Кто-то поддерживает, кто-то нет. - person Leigh; 09.01.2013

Думал, что я добавлю меньше кода к этой проблеме:

<cfset sortColumns = {IncidentID = "IncidentID", AnimalID = "AnimalID", IntakeDate = "IntakeDate", DxDate = "DxDate", OutcomeDate = "OutcomeDate"}>
<cfset sortDirections = {ASC = "ASC", DESC = "DESC"}>

<cfquery datasource="MyDSN" name="qIncidents">
    SELECT IncidentID, AnimalID, IntakeDate, DxDate, OutcomeDate
    FROM Incidents
    WHERE ShelterID = <cfqueryparam cfsqltype="cf_sql_integer" value="#Arguments.ShelterID#">
    ORDER BY #sortColumns[sortBy]# #sortDirections[sortDirection]#
</cfquery>

Где sortBy и sortDirection входят через URL-адрес или где-либо еще.

Мне это нравится, потому что это чисто, и вы не можете ничего вводить через предложение ORDER BY.

Любые комментарии?

person Adrian Lynch    schedule 24.06.2009