7

我正在努力成为一名优秀的 CF Web 开发人员,并使用<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>

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

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

关于如何安全地做到这一点的任何建议?

4

5 回答 5

13

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

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

ORDER BY #SortBy#
于 2009-05-20T17:46:19.650 回答
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>
于 2009-05-21T12:57:48.527 回答
2

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

我确实喜欢在请求变量(url、form)中使用数字来指定要排序的列,然后在开关中使用它并将其转换为实际的列名 - 这样你就不会将列名暴露给用户。

至于何时/为什么使用 cfqueryparam,请记住它不仅仅是关于输入验证和防止 SQL 注入(尽管这是一个非常好的奖励) - 使用 cfqueryparam,数据库的底层 SQL 使用 SQL 绑定通过驱动程序发送回variables - 占位符值,因此数据库优化器可以确定以更通用的格式使用哪个索引......所以当您发送这样的 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 和数据库的复杂性,这可以节省大量时间。以我的经验,它对 where 子句中的 oracle 和日期/时间列非常有用。

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

乔恩

于 2009-05-21T18:18:40.610 回答
0

关于在 MySQL 的 order 子句中使用“cfqueryparam”的评论。是的,我相信 MySQL 数据源允许这样做。虽然使用列ordinal,而不是列名(它似乎被视为一个常量字符串)。

不幸的是,它似乎根本不适用于 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 的主要目的。所以绑定变量的描述是一个很好的补充。

于 2009-05-21T16:46:07.240 回答
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 子句注入任何东西。

任何意见?

于 2009-06-24T22:02:10.067 回答