你如何在ORDER BY子句中使用cfqueryparam?

时间:2009-05-20 17:41:43

标签: sql coldfusion cfquery cfqueryparam

我正在努力成为一名优秀的CF Web开发人员,并围绕所有形成我的SQL查询的FORM或URL元素使用<cfqueryparam>

在这种情况下,我试图允许用户动态控制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>

当我这样做时,我收到以下错误:

  

由ORDER BY编号1标识的SELECT项包含一个变量,作为标识列位置的表达式的一部分。只有在引用列名的表达式进行排序时,才允许使用变量。

有关如何安全地执行此操作的任何建议吗?

5 个答案:

答案 0 :(得分:12)

不幸的是,您不能直接在Order By子句中使用CFQUERYPARAM。

如果您想动态使用Order By但仍然安全地使用Order By,您可以设置CFSWITCH或类似的结构来根据某些条件(例如,URL变量)更改SortBy变量。与往常一样,不要直接从用户传递任何值,只需查看用户的输入并根据该输入从预定的可能值列表中进行选择。然后,只使用标准语法:

ORDER BY #SortBy#

答案 1 :(得分:4)

我将扩展Aaron的答案。我做的一件事是使用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>

答案 2 :(得分:2)

使用列引用的序数值的问题是(我相信)执行create table SQL语句时的序数值 - 因此随着时间的推移将列添加到数据库表中,GUI工具用于显示列可能不代表其实际序数值。我真的会远离使用cfqueryparam。

我喜欢在请求变量(url,form)中使用数字来指定要排序的列,然后在交换机中使用它并将其转换为实际的列名称 - 所以你不要暴露你的列给用户的名字。

至于何时/为何使用cfqueryparam,请记住它不仅仅是关于输入验证和防止SQL注入(尽管这是一个非常好的奖励) - 使用cfqueryparam,数据库的基础SQL通过驱动程序发回使用SQL绑定变量 - 占位符值,因此数据库优化器可以确定以更通用的格式使用哪个索引...所以当您发送如下的SQL语句时:SELECT * FROM product WHERE ID = 1和SELECT * FROM product WHERE ID = 2优化器运行两次。但是对于绑定变量,SQL看起来像这个SELECT * FROM产品WHERE ID =? (?= 1)和SELECT * FROM产品WHERE ID =? (?= 2)因此优化器可以使用第一次分析的缓存结果来确切知道在第二个查询中使用哪个索引。根据SQL和数据库的复杂性,这可以节省大量时间。根据我的经验,使用where子句中的oracle和日期/时间列非常有用。

至于在哪里使用cfqueryparam,它可以使用SQL绑定变量......

HTH 乔恩

答案 3 :(得分:0)

以为我会在这个问题上抛出较少的代码:

<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子句注入任何东西。

有任何意见吗?

答案 4 :(得分:0)

关于在MySQL的订单条款中使用“cfqueryparam”的评论。是的,我相信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的主要目的。所以绑定变量的描述是一个很好的补充。