pf » Mastering CFQUERYPARAM
Mastering CFQUERYPARAM
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, and will convert decimal values into integers.
SELECT TOP #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.
add to del.icio.us
| Tags: cfqueryparam, cfml, coldfusion, security, sql injection, sql, cfquery
Related Entries
- Getting ColdFusion SQL Statements from SQL Server Trace - June 16, 2008
- Announcing Web Application Firewall for ColdFusion - July 9, 2007
- CFSCRIPT Cheatsheet - May 5, 2008
- ColdFusion 8 Update 1 Fixes some Image Processing Quirks - April 5, 2008
- 10 Most Useful Image Functions in ColdFusion 8 - April 5, 2008
- Preventing SQL Injections attacks and more in ColdFusion Recognized Genius
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
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.
I've also begun adding Val() to my CFQueryParam's. I.e., <cfqueryparam value="#val(form.age)#" cfsqltype="cf_sql_integer">
Of course you can/should also validate your data before getting to the query.
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.
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.
- Top 10 Reserved SQL Keywords
- Web Design Tips for Programmers
- Geolocation API for Adobe AIR?
- Dear SQL Server Enterprise Manager Developer
- PostalMethods - Web Service for Snail Mail
- Google Code Search for ColdFusion
- Speaking at CFUNITED 2008
- Getting ColdFusion SQL Statements from SQL Server Trace
RSS
Pete Freitag is a software engineer, and web developer located in










