2009-05-20 13 views
7

Sto cercando di essere un buon sviluppatore web CF e di utilizzare <cfqueryparam> attorno a tutti gli elementi FORM o URL che lo rendono alle mie query SQL.Come si utilizza cfqueryparam nella clausola ORDER BY?

In questo caso, sto tentando di consentire a un utente di controllare la clausola ORDER BY in modo dinamico.

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

Quando faccio questo, ottengo il seguente errore:

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.

Qualche suggerimento su come farlo in modo sicuro?

+0

Che database stai usando? Posso usare cfqueryparam fine con MySQL 5 –

risposta

12

Sfortunatamente, non è possibile utilizzare CFQUERYPARAM direttamente nella clausola Order By.

Se si desidera utilizzare l'ordine dinamicamente ma comunque farlo in modo sicuro, è possibile impostare un CFSWITCH o una struttura simile per modificare la variabile SortBy in base ad alcune condizioni (ad esempio, una variabile URL). Come sempre, non passare alcun valore direttamente dall'utente, basta guardare l'input dell'utente e selezionare da un elenco predeterminato di valori possibili basati su quello. Quindi, usa la sintassi standard:

ORDER BY #SortBy# 
+2

Un buon modo di pensare a cosa è possibile e non è possibile utilizzare un cfqueryparam per è pensarlo in termini di dati presenti nel database. La variabile sortBy è al posto di una colonna, non i dati nella colonna. Non è inoltre possibile utilizzare un queryparam per parametrizzare la tabella o l'elenco di colonne. –

+0

Che database stai usando? Posso usare cfqueryparam nella clausola dell'ordine fine con MySQL –

+0

Sto usando MSSQL –

4

Espanderò semplicemente la risposta di Aaron. Una delle cose che faccio è quello di utilizzare listfindnocase() per fare in modo che gli argomenti passati per la clausola ORDER BY sono validi:

<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

Sono d'accordo. Questo è un buon approccio. Ho migliorato leggermente il tuo codice, aggiungendo variabili che sono esplicitamente sicure e che non possono mai contenere valori specificati dall'utente. Non pensavo che valesse una risposta separata, ed era più facile mostrarti che cercare di spiegare in un commento. Se non ti piace quello che ho fatto, per favore rotola indietro. –

0

quanto riguarda il commento sull'uso "cfqueryparam nella bella clausola di ordine con MySQL" . Sì, credo che sia consentito con le origini dati MySQL. Sebbene si utilizzi la colonna ordinale, non il nome della colonna (che invece sembra essere una stringa costante).

Sfortunatamente, non sembra funzionare per le origini dati MS SQL. Almeno non da quello che posso dire.

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

Aggiornamento: Per quanto riguarda i commenti su ordinale: No, credo che si riferisce alla posizione della colonna nell'elenco di selezione, non la tabella sottostante. Quindi dovrebbe andare bene.

Sì, sono d'accordo che sql injection protection non è lo scopo principale di cfqueryparam. Quindi la descrizione delle variabili di binding è stata una buona aggiunta.

+0

Niente di inesatto sulla risposta, per quanto posso vedere. Chiedete a un elettore anonimo di lasciare un commento? – Leigh

+0

Forse il 'ORDER BY 3' sembrava confuso. Se funziona su MySQL e si nota che funziona su MySQL, questa è una soluzione elegante al problema –

+0

In realtà, utilizzando i driver di database predefiniti funziona con MySQL ma * not * con MS SQL. Quando si tratta di utilizzare oridinals, le persone tendono ad amarlo o ad odiarlo. Ho sentimenti contrastanti. È più elegante delle alternative, ma non sempre intuitivo. Guardando indietro, ho il sospetto che ho postato questo per confutare diverse affermazioni generali che non è mai possibile, il che non è esattamente vero. Dipende dal guidatore. Alcuni lo supportano, altri no. – Leigh

2

il problema con l'utilizzo del valore ordinale per un riferimento di colonna è che è (credo) il valore ordinale al momento dell'esecuzione dell'istruzione SQL della tabella di creazione, così come si aggiungono colonne alla tabella del database nel tempo, la GUI lo strumento che usi per visualizzare le colonne potrebbe non rappresentare il suo valore ordinale reale. starei davvero lontano dall'usare cfqueryparam per questo.

Mi piace l'idea di utilizzare un numero nelle variabili di richiesta (url, modulo) per specificare quale colonna ordinare e quindi utilizzarlo nell'interruttore e tradurlo in un nome di colonna effettivo, in modo da non esporre il proprio nomi di colonne per l'utente.

fino a quando/perché utilizzare cfqueryparam, tenere a mente il suo NON solo sulla convalida dell'input e prevenire l'iniezione SQL (anche se questo è un bonus molto bello) - con cfqueryparam l'SQL sottostante al database viene inviato di nuovo attraverso driver che utilizza variabili bind SQL: valori segnaposto, quindi l'ottimizzatore di databse può determinare quale indice utilizzare in un formato più generico ...quindi quando si invia un'istruzione SQL come questa: SELECT * FROM prodotto WHERE ID = 1 e SELECT * FROM prodotto WHERE ID = 2 l'ottimizzatore esegue entrambe le volte. ma con le variabili di collegamento, l'SQL è simile a questo SELECT * FROM product WHERE ID =? (? = 1) e SELECT * FROM prodotto WHERE ID =? (? = 2) in modo che l'ottimizzatore possa utilizzare i risultati memorizzati nella cache della prima analisi per sapere esattamente quale indice utilizzare nella seconda query. a seconda della complessità di SQL e del database, questo può essere un enorme risparmio di tempo. nella mia esperienza, le sue prestazioni molto utili sono con le colonne oracle e date/time nella clausola where.

in modo, per quanto dove utilizzare cfqueryparam, il suo dove può essere utilizzata una variabile SQL vicolo cieco ...

hth Jon

0

Ho pensato di buttare codice minore a questo 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> 

Dove sortBy e sortDirection arrivano tramite l'URL o dove mai.

Mi piace perché è pulito e non è possibile iniettare nulla tramite la clausola ORDER BY.

Eventuali commenti?

Problemi correlati