Mastering CFQUERYPARAM

coldfusion

If you haven't been using the cfqueryparam tag, chances are you had a baptism by fire this week. As you may have heard, lots of ColdFusion powered sites were targeted by hackers using SQL Injection this week.

Fortunately SQL Injection is very easy to prevent in CFML using the cfqueryparam tag, and many people have pointed out some of the simple use cases for the tag. But there are a few cases where you can't use the cfqueryparam tag. In those cases, ColdFusion might throw an exception that looks like this:

[Macromedia][SQLServer JDBC Driver][SQLServer]Incorrect syntax near '@P1'.
[Macromedia][SQLServer JDBC Driver][SQLServer]Statement(s) could not be prepared.

Let's take a look at some of these special cases, and how to get around them:

SELECT TOP

If you are passing a variable into a SELECT TOP statement, you can't use cfqueryparam, instead consider using the Val function. This is a really handy function that will return 0 whenever it gets a non-numeric value. The Val function still allows decimal values, we can convert those to an integer using the Ceiling function, which rounds up to the nearest integer.

SELECT TOP #Ceiling(Val(url.max_rows))# first_name FROM people

ORDER BY

When attempting to use a cfqueryparam tag in the ORDER BY statement you might receive an error such as:

[Macromedia][SQLServer JDBC Driver][SQLServer]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.

A good way to get around this limitation is to use the ListFindNoCase function, to limit the sortable column names, for example:

<cfset sortable_column_list = "age,height,weight,first_name">
<cfquery ...>
  SELECT first_name, age, height, weight
  FROM people
  ORDER BY <cfif ListFindNoCase(sortable_column_list, url.sort_column)>#url.sort_column#<cfelse>first_name</cfif>
</cfquery>

Passing Value Lists using IN

What to do when your variable contains a list of values to be used with a SQL IN expression? The cfqueryparam actually makes it very easy to pass a list, you don't even need to put single quotes around each element if for text lists, it takes care of that for you. To use cfqueryparam with an IN simply add list="true" to your cfqueryparam tag.

<cfset name_list = "Bob,Fred,Pete">
<cfquery ...>
  SELECT first_name, age, height, weight
  FROM people
  WHERE first_name IN (<cfqueryparam value="#name_list#" list="true" cfsqltype="cf_sql_varchar">)
</cfquery>

Cached Queries

ColdFusion 8 now allows cfqueryparam in cached queries, but if you are running earlier versions, you won't be able to use it with cached queries.

If the variables passed into the query are integer only, then you can use the Val function to protect against SQL Injection. Or if the possible string values are limited you can use the ListFindNoCase function as shown above.

The best workaround is to remove the caching, upgrade to CF8, or cache them in the application scope, as follows:

<cfif NOT IsDefined("application.my_cached_query")>
  <cfquery name="application.my_cached_query">
    ...
  </cfquery>
</cfif>

This will keep the query cached until the application is reinitialized, or the variable is overwritten.

Passing NULL's

The cfqueryparam lets you pass null values into your database using the null="true" attribute. For example:

UPDATE people
SET age = <cfif IsValid("integer", form.age) AND form.age NEQ 0>
  <cfqueryparam value="#form.age#" cfsqltype="cf_sql_integer">
<cfelse>
  <cfqueryparam null="true" cfsqltype="cf_sql_integer">
</cfif>

Those are some of the more common gotcha's that you will run into with cfqueryparam. Please post a comment with any other cfqueryparam tricks, or special cases.



Related Entries

6 people found this page useful, what do you think?

 Download FuseGuard WAF for ColdFusion

Trackbacks

Trackback Address: 677/024023A66DB11C1D5B4AF7E273F15E87

Comments

On 07/24/2008 at 9:10:19 PM EDT William from Lagos wrote:
1
Good post Pete. ColdFusion Muse has a couple of equally good posts on this subject

http://www.coldfusionmuse.com/index.cfm/2008/7/21/SQL-injection-using-order-by

http://www.coldfusionmuse.com/index.cfm/2008/7/18/Injection-Using-CAST-And-ASCII

http://www.coldfusionmuse.com/index.cfm/2008/7/21/query-string-with-cfqueryparam

On 07/25/2008 at 9:47:26 AM EDT Pete Freitag wrote:
2
Thanks William, didn't see those!

On 07/25/2008 at 11:11:40 AM EDT Chris Bestall wrote:
3
"This is a really handy function that will return 0 whenever it gets a non-numeric value, and will convert decimal values into integers."

Actually, val() does not convert decimal values to an integer.

From the livedocs: Converts numeric characters that occur at the beginning of a string to a number.

On 07/25/2008 at 12:33:44 PM EDT Steve Withington wrote:
4
As always, good stuff Pete!

I've also begun adding Val() to my CFQueryParam's. I.e., <cfqueryparam value="#val(form.age)#" cfsqltype="cf_sql_integer">

On 07/25/2008 at 12:45:16 PM EDT Pete Freitag wrote:
5
Thanks Steve, using Val in your cfqueryparam's not a bad idea because it prevents exceptions from being thrown when users try to pass invalid data. I'm sure that's why you are doing it, I just wanted to point that out for everyone else's benefit.

Of course you can/should also validate your data before getting to the query.

On 07/25/2008 at 12:52:52 PM EDT Seb wrote:
6
The neater way to use the null attribute is this:

UPDATE people SET age = <cfqueryparam value="#form.age#" null="#not IsValid("integer", form.age) OR form.age EQ 0)#" cfsqltype="cf_sql_integer" />

If the expression evaluates to TRUE, the null overrides the value.

On 07/25/2008 at 1:34:17 PM EDT Pete Freitag wrote:
7
Neat Trick Seb. I didn't know the null attribute would override the value attribute when true.

On 08/07/2008 at 10:42:12 PM EDT WilGeno wrote:
8
Beware of information exposure. Many times I see sites that pass actual column names on the url string as part of a sort field or a table id column. This just give more information to hackers if they decide to target your site. Instead pass variables that are not the name of tables or columns and us code to handle the rest. ie. Sorting select * from products order by <cfswitch expression="#url.sortby#"> <cfcase value="pname"> product_name </cfcase> <cfcase value="price"> retail_price </cfcase> </cfswitch> By doing this your not exposing table or column names to the world. The lesson is to limit your exposure. Wil Genovese Sr. Web Application Developer

On 08/24/2008 at 6:32:00 PM EDT Larry Rogers wrote:
9
In your code examples you have list="true" and in another place null="true" shouldn't these be list="yes" and in the other place null="yes"? I'm new to this and really don't know. Thanks

On 08/25/2008 at 11:10:53 AM EDT Pete Freitag wrote:
10
@Larry - I prefer using the string true/false for boolean values. ColdFusion typically allows either yes/no or true/false in boolean values, so it's just a personal preference.

On 08/28/2008 at 6:05:13 PM EDT Random Thoughts wrote:
11
Just a comment about something I noticed in the PASSING NULL'S section above...

You have form.age NEQ 0 in the CFIF statement, but don't forget that we are all zero for the first year of our lives. ;)

If you have forms where a parent might be entering info about children, zero would actually be a valid value.

On 10/26/2009 at 10:28:18 AM EDT Adam Rifat wrote:
12
Good tip about the list="true" flag. Thanks!

On 08/25/2010 at 9:14:51 PM EDT Chris Bowyer wrote:
13
Thanks for the tip on passing nulls

On 09/15/2010 at 5:45:14 PM EDT Deborah wrote:
14
thanks! that really helped me with passing value lists using in. Though I had already written a special function to prepare my list data with quotes then you tell me that list = "true" prepares it for me!

On 07/28/2011 at 6:44:45 PM EDT Mike Henke wrote:
15
Peter - what is your take on using or not using CFSQLType attribute with CFQueryParam?

On 12/06/2012 at 10:32:01 PM EST Luka wrote:
16
Jen thank you very much for the tutorial. This is my first time cimnobe jquery and coldfusion. I used your codes cimnobed the first 4 pieces and saved them as index.cfm, the 5th is states.cfm and the 6th is states_abbrev.cfm. I created a table call states with 3 variables: id, state, and abbrev but I could not get the autocomplete to work. Would you please let me know what I did wrong? Here are my files:=======index.cfm = I downloaded the stylesheet as well as jquery.min.js and jquery-ui.min.js and have them on my local box======= $(function() { $( #abbrev').val( ); $( #state ).autocomplete({ source: states.cfm , minLength: 2, select: function(event, ui) { $( #state_id').val(ui.item.id); $( #abbrev').val(ui.item.abbrev); } }); $( #state_abbrev ).autocomplete({ source: states_abbrev.cfm , minLength: 2 }); });jQuery UI Autocomplete Example ColdFusion BackendStart typing the name of a state or territory of the United StatesState (abbreviation in separate field): State (replaced with abbreviation): =====states.cfm = I changed the datasource to mydata name and also run a test to see if it pulls data and yes, it does.===== Select * from states where state like %%'#serializeJSON(returnArray)#============states_abbrev.cfm = I changed the datasource to mydata name and also run a test to see if it pulls data and yes, it does.=========== Select * from state where state like %#URL.term#%'#serializeJSON(returnArray)#

Post a Comment




  



Spell Checker by Foundeo

Recent Entries



foundeo


did you hack my cf?