2009-05-20 6 views
7

Estoy tratando de ser un buen desarrollador web de CF y uso <cfqueryparam> alrededor de todos los elementos FORM o URL que lo hacen a mis consultas SQL.¿Cómo se usa cfqueryparam en la cláusula ORDER BY?

En este caso, estoy tratando de permitir que un usuario controle dinámicamente la cláusula 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> 

Cuando hago esto, me sale el siguiente error:

The SELECT item identified by the ORDER BY number 1 contains a variable as part of the expression identifying a column position. Variables are only allowed when ordering by an expression referencing a column name.

¿Alguna sugerencia sobre cómo hacer esto de manera segura?

+0

¿Qué base de datos está utilizando? Puedo usar cfqueryparam fine con MySQL 5 –

Respuesta

12

Desafortunadamente, no se puede usar CFQUERYPARAM directamente en la cláusula Order By.

Si desea utilizar el orden por dinámicamente pero aún hacerlo de manera segura, puede configurar un CFSWITCH o una estructura similar para cambiar su variable SortBy dependiendo de alguna condición (por ejemplo, una variable URL). Como siempre, no pase ningún valor directamente del usuario, simplemente mire la entrada del usuario y seleccione de una lista predeterminada de valores posibles en función de eso. Entonces, solo use la sintaxis estándar:

ORDER BY #SortBy# 
+2

Una buena forma de pensar sobre lo que puede y no puede usar un cfqueryparam es pensar en los datos que están en la base de datos. La variable sortBy está en lugar de una columna, no datos en la columna. Tampoco podría usar un parámetro de consulta para parametrizar la tabla o la lista de columnas. –

+0

¿Qué base de datos está usando? Puedo usar cfqueryparam en la cláusula order fine con MySQL –

+0

Estoy usando MSSQL –

4

Voy a ampliar la respuesta de Aaron. Una de las cosas que hago es usar listfindnocase() para asegurarse de que los argumentos que se pasan a la cláusula ORDER BY son válidas:

<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> 
+0

Estoy de acuerdo. Ese es un buen enfoque. Mejoré un poco tu código, añadí variables que son explícitamente seguras y que nunca pueden contener valores especificados por el usuario. No pensé que valiera la pena una respuesta por separado, y era más fácil mostrarte que tratar de explicarlo en un comentario. Si no te gusta lo que hice, por favor hazlo retroceder. –

0

En cuanto al comentario sobre el uso de "cfqueryparam de la multa cláusula de orden con MySQL" . Sí, creo que está permitido con fuentes de datos MySQL. Aunque utiliza la columna ordinal, no el nombre de columna (que parece tratarse como una cadena constante en su lugar).

Desafortunadamente, no parece funcionar en absoluto para las fuentes de datos MS SQL. Al menos no por lo que puedo decir.

<!--- 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#"> 

Actualización: En cuanto a los comentarios sobre ordinal: No, yo creo que se refiere a la posición de la columna en la lista de selección, no la tabla subyacente. Entonces debería estar bien.

Sí, estoy de acuerdo en que la protección de inyección sql no es el objetivo principal de cfqueryparam. Entonces la descripción de las variables de vinculación fue una buena adición.

+0

Nada impreciso acerca de la respuesta hasta donde puedo ver. Anónimo abajo votante cuidado para dejar un comentario? – Leigh

+0

Tal vez el 'ORDEN POR 3' parecía confuso. Si funciona en MySQL y nota que funciona en MySQL, esta es una solución elegante al problema –

+0

En realidad, usando los controladores de base de datos predeterminados funciona con MySQL pero * no * con MS SQL. Cuando se trata de usar oridinales, las personas tienden a amarlo u odiarlo. Tengo sentimientos encontrados Es más elegante que las alternativas, pero no siempre es tan intuitivo. Mirando hacia atrás, sospecho que publiqué esto para refutar varias declaraciones generales que nunca es posible, lo cual no es exactamente cierto. Depende del conductor. Algunos lo apoyan, otros no. – Leigh

2

el problema con el uso del valor ordinal para una referencia de columna es (creo) el valor ordinal en el momento en que se ejecutó la declaración SQL de tabla de creación, para agregar columnas a la tabla de base de datos en el tiempo la herramienta que usa para mostrar las columnas puede no representar su valor ordinal real. Realmente me mantendría alejado de usar cfqueryparam para esto.

me gusta la idea de utilizar un número en las variables de solicitud (url, formulario) para especificar qué columna ordenar y luego usar eso en el interruptor y traducirlo a un nombre de columna real - para que no exponga su nombres de columna para el usuario.

en cuanto a cuándo/por qué utilizar cfqueryparam, tenga en cuenta que NO solo se trata de validación de entrada y prevención de inyección SQL (aunque es una bonificación muy buena) - con cfqueryparam el SQL subyacente a la base de datos se envía a través del controlador que usa variables de vinculación SQL - valores de marcador de posición, por lo que el optimizador de databse puede determinar qué índice usar en un formato más genérico ...por lo tanto, cuando envíe una declaración SQL como esta: SELECCIONE * FROM producto DONDE ID = 1 y SELECCIONE * FROM producto DONDE ID = 2 el optimizador se ejecuta ambas veces. pero con las variables de vinculación, el SQL se ve así: SELECT * FROM producto WHERE ID =? (? = 1) y SELECCIONAR * FROM producto WHERE ID =? (? = 2) para que el optimizador pueda usar los resultados almacenados en caché del primer análisis para saber exactamente qué índice usar en la segunda consulta. Dependiendo de la complejidad del SQL y de la base de datos, esto puede ser un gran ahorro de tiempo. en mi experiencia, es muy útil en cuanto a rendimiento, con oráculo y columnas de fecha/hora en la cláusula where.

En lo que a dónde utilizar cfqueryparam, su donde se puede utilizar una variable de SQL de vinculación ...

hth Jon Pensamiento

0

me tiraría código de menor a este problema:

<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> 

Donde sortBy y sortDirection entran a través de la URL o donde sea.

Me gusta porque está limpio y no se puede inyectar nada mediante la cláusula ORDER BY.

¿Algún comentario?

Cuestiones relacionadas