Ask Ben: Protecting Database Table Names In ColdFusion CFQuery

Posted July 26, 2007 at 2:33 PM by Ben Nadel

Tags: ColdFusion, Ask Ben

This may be to elementary for an "Ask Ben" but I cannot find information anywhere on how to protect a query when a table name is passed in via a form or url. For Example:

<cfquery name="xyz" datasource="ds123">
UPDATE #form.table#
SET active = <cfqueryparam cfsqltype="cf_sql_bit" value="1">
</cfquery>

I cannot put the table name in a cfqueryparam, I tried, wasn't sure if it would work or not. Am I stuck making a quick function to verify that value is a single word and if its not return some bogus table name? Thanks for any help.

Let me start off by saying that, while I do not know what your exact situation is, this is something that makes me very nervous! If this is for your own personal use, that is one thing, but something like this should NEVER be available in any sort of public capacity as it will leave you open to different types of malicious behavior! Red Flag! Red Flag!

That being said, in order to make sure that the table is valid, we just have to check it against a set of explicit table names. This is the best way to make sure that only valid table names can ever be entered. If anyone ever tries to mess with the FORM-based table name, at least the ColdFusion CFQuery tag will never execute. Furthermore, before we even examine the table name, let's strip out as many invalid characters as we can.

  • <!--- Param the table name in the FORM scope. --->
  • <cfparam
  • name="FORM.table"
  • type="string"
  • default=""
  • />
  •  
  •  
  • <!---
  • Before we even check anything, let's clean characters
  • out of the table name that we KNOW cannot be valid.
  • Things like single quotes and semi colons might be a
  • sign of a SQL injection attack. Let's strip out anything
  • that is not standard.
  • --->
  • <cfset FORM.table = FORM.table.ReplaceAll(
  • "[^\w\-_]+",
  • ""
  • ) />
  •  
  • <!---
  • Passing in a database Table as a variable is VERY
  • risky because it leaves you open to all kinds of
  • malicious attacks and tomfoolery. Therefore, we
  • have to be ultra careful about how we handle this.
  • Make sure that the table is a valid table and
  • only allow table names that are in the following list.
  • --->
  • <cfsavecontent variable="strTableList">
  • address
  • contact
  • phone
  • notes
  • </cfsavecontent>
  •  
  • <!---
  • Check to see if the table is valid. Tread the space,
  • tab, line break, and carriage return all as list
  • delimiters - this just makes the list easier to read.
  • Also, do NOT use NOCASE search. Let proper name
  • case be yet another layer of security.
  • --->
  • <cfif ListFind(
  • strTableList,
  • FORM.table,
  • " #Chr( 9 )##Chr( 13 )##Chr( 10 )#"
  • )>
  •  
  • <!---
  • Get the record count for the given table. When
  • putting in the table name, be sure to use the []
  • notation so that we do not get any invalid table
  • name exceptions.
  • --->
  • <cfquery name="qTable" datasource="#REQUEST.DSN.Source#">
  • SELECT
  • COUNT( * ) AS count
  • FROM
  • [#FORM.table#]
  • </cfquery>
  •  
  •  
  • <!--- Output record count. --->
  • <p>
  • Records in #FORM.table#: #qTable.count#
  • </p>
  •  
  • <cfelse>
  •  
  • <!---
  • A valid table name was NOT found. Throw an error
  • or handle in some other way.
  • --->
  • <cfthrow
  • type="TableNotFoundException"
  • message="The table [#FORM.table#] is not valid"
  • />
  •  
  • </cfif>

Give that a go... and again, I would recommend this only for personal use.



Reader Comments

Jul 26, 2007 at 3:27 PM // reply »
42 Comments

I'm not sure how you go about it with other DBMS, but if you are using MS SQL you have the ability to query the DB for a list of the tablenames:

SELECT name
FROM dbo.sysobjects
WHERE (xtype = 'U')


Jul 26, 2007 at 3:36 PM // reply »
15 Comments

Each rdbms has its own way of storing and giving you access to its database metadata. However, you could achieve the same above with dynamic table names using CF8's new cfdbinfo tag.


Jul 26, 2007 at 3:38 PM // reply »
11,314 Comments

I was thinking about bringing up some sort of introspection, but I just felt that explicitly listing out "valid" tables is the safest thing to do (that, and I have never done any kind of introspection in that way, so I didn't want to give misinformation on it). Plus, with explicit name, you don't have to allow access to all DB tables, just a select set - this might help to minimize any damage that someone might be trying to cause.


Jul 26, 2007 at 3:48 PM // reply »
12 Comments

You could also:
- create an array or structure mapping the table names
- have a drop down with the array number or key as a value
- then when submitted if the submitted value is not in the array or structure you know you have a problem!

cfdbinfo will also help with this process in the future.


Jul 26, 2007 at 4:05 PM // reply »
11 Comments

Ben,

Good post. That is very true that if we specify explicit names of tables it is the safest. Let us see with real example that SQL can do the table exist check how application help database with the task.

We are using application where we create lots of dynamically created tables, all are real tables and all are dropped when day long running processes is over.

We use frequent check in our system to make sure that table exist (and safe).

IF EXISTS(SELECT name
FROM sys.tables
WHERE name = 'yourtablename')
--Processes goes here.

Interesting that we have all the table with specific prefix and we check in ColdFusion using list function listfindnocase . If this CF check fails, we even do not go to query part.

Regards,
Pinal


Jul 27, 2007 at 10:48 AM // reply »
2 Comments

This maybe a bit of dirty code: (but it cannot be any dirtier than allready described by the requestor)
Why not make a list of "safe" to edit tables and check with listFindNoCase(application.safeToEditTableList,form.tablename) or do a cfswitch with the tableaname?


Jul 27, 2007 at 12:06 PM // reply »
79 Comments

Ben,

You make a good point about what to do to enforce validity, but I disagree that you should /never/ do it for customer use.

I think variable table names are a good way to avoid duplication in your code in some situations, or to avoid long logic chains.


Jul 27, 2007 at 12:18 PM // reply »
11,314 Comments

@Sam,

While I agree that variable names is a good thing and can be used to avoid duplicate effort and all that goodness, I guess what rubbed me the wrong way was the fact that the table name was passed via the URL / FORM scopes. This just feels so wrong (and not in the good way). Of course, I don't know the context it is being used, so I cannot say for sure; however, you have to take into account things like people hacking the FORM and changing value (something that FireFox makes SOOO easy).

Context is very important, but it still rubs me the wrong way.


Jul 27, 2007 at 2:42 PM // reply »
79 Comments

Well, I was thinking about passing it through the form. For instance, if you need to do a report from one of several different tables, you might allow them a select form field with table names as values.

I haven't actually used that, but I have allowed them to select column names to report on. Of course, I don't do much work on public websites where this would be a problem - I do mostly backend work for specific clients where the general public doesn't have access, so my perspective is different.

In the case where you do find it easier to allow table and column names, it is good to check them against specific values - if anything you can report an error message without doing try/catch or worse, just letting the unfriendly error message show up to the user.


Jul 28, 2007 at 5:23 PM // reply »
11,314 Comments

@Sam,

Even in a case where you let people select table columns for a report, I wouldn't inject the table name directly into the SQL statement. Instead, I would use some logic around the column name. For example, instead of just doing something like this:

SELECT #column_list# FROM [table]

... I would rather do something like this:

SELECT
<cfif ListFind( "id", column_list )>id,</cfif>
<cfif ListFind( "name", column_list)>name,</cfif>
.....
FROM [table]

This way, you can still let the user select random columns, but you enforce the column choices by checking their existence in logic, rather than just dumping output. Yes, my way does require more logic, but I just feel better about it. Plus, there is usually SQL optimization that you can do around the table columns - for instance, the absence of a particular column name might mean you don't have to perform a particular join.


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
Jun 19, 2013 at 2:01 PM
Experimenting With The Amazon Simple Storage Service (S3) API Using ColdFusion
I have coincidentally been beating my head against the S3 API for the last week or so. One big "gotcha" I had to work around was file names and paths containing spaces. Remember to URL Enco ... read »
Jun 19, 2013 at 1:27 PM
Using Slice(), Substring(), And Substr() In Javascript
very good article. By the way IE supports negative values in substr or slice in verson 10. ... read »
Jun 19, 2013 at 11:33 AM
Filter vs. ngHide With ngRepeat In AngularJS
In your assessment, is it correct to say that given a list of say 500 items its more performant to use the `ngHide` method over the `filter` method? ... read »
Jun 19, 2013 at 10:18 AM
ColdFusion Path Usage And Manipulation Overview
Anyone happen to know if the file created by getTempFile will be automatically removed at any point? Nothing mentioned in the docs, and restarting CF doesn't remove them, so it seems it needs manu ... read »
Jun 19, 2013 at 9:41 AM
Working With Inherited Collections In AngularJS
I actually just ran into this same situation with a demo I was putting together. Your implementation of multi-lvl $scope's > Mine :) ... read »
Jun 19, 2013 at 8:17 AM
My Experience With AngularJS - The Super-heroic JavaScript MVW Framework
@Prateek, to match a word or text you should use .toContain('word') that's a jasmine reference. website is : http://pivotal.github.io/jasmine/ ... read »
Jun 19, 2013 at 8:10 AM
My Experience With AngularJS - The Super-heroic JavaScript MVW Framework
Hi Guys, Actually i am doing e2e test of angular js of my project but i am not getting one thing that is how to press enter key through the test when my form is filled as i am not using a button but ... read »
Jun 18, 2013 at 9:20 PM
Mapping AngularJS Routes Onto URL Parameters And Client-Side Events
I couldn't find examples of passing multiple arguments using the when() routing statement so figured out through trial and error that you can pass multiple arguments using the following format: .whe ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools