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.
Launch code in new window » Download code as text file »
Give that a go... and again, I would recommend this only for personal use.
Download Code Snippet ZIP File
Comments (10) | Post Comment | Ask Ben | Permalink | Print Page
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')
Posted by Dustin on Jul 26, 2007 at 3:27 PM
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.
Posted by Brian Rinaldi on Jul 26, 2007 at 3:36 PM
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.
Posted by Ben Nadel on Jul 26, 2007 at 3:38 PM
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.
Posted by Sam Farmer on Jul 26, 2007 at 3:48 PM
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
Posted by Pinal Dave on Jul 26, 2007 at 4:05 PM
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?
Posted by Jorrit Janszen on Jul 27, 2007 at 10:48 AM
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.
Posted by Sam on Jul 27, 2007 at 12:06 PM
@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.
Posted by Ben Nadel on Jul 27, 2007 at 12:18 PM
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.
Posted by Sam on Jul 27, 2007 at 2:42 PM
@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.
Posted by Ben Nadel on Jul 28, 2007 at 5:23 PM