I have been tasked with going through a number of ColdFusion sites that have recently been the subject of a rather nasty SQL Injection attack. Basically my work involves adding <cfqueryparam> tags to all of the inline sql. For the most part I've got it down, but can anybody tell me how to use cfqueryparam with the LIKE operator?
If my query looks like this:
select * from Foo where name like '%Bob%'
what should my <cfqueryparam> tag look like?
#Joel, I have to disagree.
select a,b,c
from Foo
where name like <cfqueryparam cfsqltype="columnType" value="%#variables.someName#%" />
Never suggest to someone that they should "select star." Bad form! Even for an example! (Even copied from the question!)
The query is pre-compiled and you should include the wild card character(s) as part of the parameter being passed to the query. This format is more readable and will run more efficiently.
When doing string concatenation, use the ampersand operator (&), not the plus sign. Technically, in most cases, plus will work just fine... until you throw a NumberFormat() in the middle of the string and start wondering why you're being told that you're not passing a valid number when you've checked and you are.
select a,b,c
from Foo
where name like <cfqueryparam cfsqltype="cf_sql_varchar" value="%Bob%" />;
Related
I have a table with values under a column named:str_condition
values in this column can be : variables.bit_male / application.bit_male / isdefined('session.int_user_id')
The value can be complex as it can be.
I need to use the value of the values in the column.
Currently, what I am doing is
<cfif evaluate(query.str_condition) eq true>
.....code....
</cfif>
Now, I need to omit the evaluate.
TBH, I'd stick with evaluate() for this: you're leveraging one of the few situations it makes sense. Provided what you have in the DB field is just an expression (no tags), then evaluate() will work fine.
As others have suggested... storing expressions in the DB is not ideal, but I can see how sometimes it might be the best approach, However do reconsider it though, in case you can come up with a different approach entirely (this is situation-specific, so we can't really give you guidance on that).
The only other real option for you would be to write the code from the DB to file then include it, but that would be a worse solution than just using evaluate(), I think.
A lot of people get hung up on the dogma that is evaluate() is bad, without really stopping to think about why that's the case... it's unnecessary for most situations people use it, but it's completely fine in situations in which it is needed (such as yours).
This is an edited answer, since I originally misread the question.
In many cases, array notation is your freind
<cfif queryname['fieldname'][rownumber] is true>
code for true
Note that the queryname is not quoted but the fieldname is. If you don't quote the fieldname, ColdFusion will assume it's a variable.
Also pertinent is that if you are storing things in a database, such as code, that you want to select and then execute, you have to select those things, write them to another .cfm file, and then cfinclude that file. That's somewhat inefficient.
In your case, you are storing variable names in your database. If using evaluate is giving you the correct results, anything you change would likely be a change for the worse.
How many unique combinations exist in the database? And do new values show up without developer interaction?
If it's a reasonable number of possible values that don't change then use a switch statement and write the line of code that handles each possible value.
<cfswitch expression="#query.str_condition#">
<cfcase value="variables.bit_male">
<cfset passed = variables.bit_male>
</cfcase>
<cfcase value="application.bit_male">
<cfset passed = application.bit_male>
</cfcase>
<cfcase value="isdefined('session.int_user_id')">
<cfset passed = isdefined('session.int_user_id')>
</cfcase>
<cfdefaultcase>
<cfset passed = false>
</cfdefaultcase>
</cfswitch>
<cfif passed>
.....code....
</cfif>
You don't have to hand write all of them, you can use a sql query to generate the repetitive part of the coldfusion code.
SELECT DISTINCT '<cfcase value="' + replace(table.str_condition,'"','""') + '"><cfset passed = ' + table.str_condition + '></cfcase>' as cfml
FROM table
ORDER BY len(str_condition), str_condition
If I am reading this correctly you are not just storing variable names in the database but actual snippets of code such as [ isDefined(session.it_user_id) ].
If this is what you are doing then you need to stop and rethink what you are trying to achieve. Storing code in your database and using evaluate to execute it is an incredibly bad idea.
It sounds to me like you are trying to create a generic code block that you can copy paste in multiple places and just set your conditional logic in the db.
The short answer is not to find a way around using evaluate but to stop storing code in your database full stop.
I am creating a query using cfscript syntax, and I have two query parameters that are dates. I created the date string the first time using
queryservice.addParam(
name="last_update",
value="createODBCDate(now())",
cfsqltype="cf_sql_date");
I would assume this would an analogue to:
<cfqueryparam value="#createODBCDate(now())#" cfsqltype="cf_sql_date">
So, when I run the query, I'm getting:
The cause of this output exception was that: coldfusion.runtime.Cast$DateStringConversionException: The value createODBCDate(now()) cannot be converted to a date.
Fine. So I created a variable,
var currentDate = createODBCDate(now());
added it to
queryservice.addParam(
name="last_update",
value="createODBCDate(now())",
cfsqltype="cf_sql_date");
and got
The cause of this output exception was that: coldfusion.runtime.Cast$DateStringConversionException: The value currentDate cannot be converted to a date.
When I created the query using the standard <cfquery ... syntax it worked fine.
So, I'm assuming that I am doing something wrong, but I can't for the life of me figure out what that is.
By the way, this is really my first time trying to create a query using the <cfscript> syntax.
value="createODBCDate(now())"
You forgot the # signs around the function. Without those, it is just a string. So the function is never invoked and you end up passing in the literal characters "createODBCDate(now())" as the date value.
Update:
As an aside, cf_sql_date automatically removes any time portion. So while using createODBCDate will not hurt anything, it is redundant. You could simply write:
queryservice.addParam(
name="last_update",
value="#now()#",
cfsqltype="cf_sql_date");
Your second attempt needs #'s just like #Leigh mentioned and also didn't reference the variable "currentDate" that you created.
I'm wondering if the # symbol is enough.
This is a part of the sql command that I'm using
WHERE login='#FORM.login#' AND password COLLATE Latin1_General_CS_AS = '#FORM.password#'
I'm trying to test it with user names such as ' OR 1=1 and variants of it, but even though it's not working I don't want to have a false sense of security.
I've read that using <cfqueryparam> can prevent this form of attack, are there any other ways?
The way to go is <cfqueryparam>. It's simple, straight-forward, datatype-safe, can handle lists (for use with IN (...)) and can handle conditional NULLs. Plus you get a benefit out of it in loops - the query text itself is sent to the server only once, with each further loop iteration only parameter values are transferred.
You can use '#var#' and be relatively safe. In the context of a <cfquery> tag ColdFusion will expand the value of var with single quotes escaped, so there is some kind of automatic defense against SQL injection. But beware: This will — by design — not happen with function return values: For example, in '#Trim(var)#' single quotes won't be escaped. This is easily overlooked and therefore dangerous.
Also, it has a disadvantage when run in a loop: Since variable interpolation happens before the SQL is sent to the server, ColdFusion will generate a new query text with every iteration of a loop. This means more bytes over the wire and no query plan caching on the server, as every query text is different.
In short: Use <cfqueryparam> wherever you can:
WHERE
login = <cfqueryparam value="#FORM.login#" cfsqltype="CF_SQL_VARCHAR">
AND password = <cfqueryparam value='#Hash(FORM.password, "SHA-512")#' cfsqltype="CF_SQL_VARCHAR">
Instead of a simple Hash(), you should indeed use a salted hash, as #SLaks pointed out in his comment.
An even better way to go would be to use stored procedures for everything.
How I can encode/escape a varchar to be more secure without using cfqueryparam? I want to implement the same behaviour without using <cfqueryparam> to get around "Too many parameters were provided in this RPC request. The maximum is 2100" problem. See: http://www.bennadel.com/blog/1112-Incoming-Tabular-Data-Stream-Remote-Procedure-Call-Is-Incorrect.htm
Update:
I want the validation / security part, without generating a prepared-statement.
What's the strongest encode/escape I can do to a varchar inside <cfquery>?
Something similar to mysql_real_escape_string() maybe?
As others have said, that length-related error originates at a deeper level, not within the queryparam tag. And it offers some valuable protection and therefore exists for a reason.
You could always either insert those values into a temporary table and join against that one or use the list functions to split that huge list into several smaller lists which are then used separately.
SELECT name ,
..... ,
createDate
FROM somewhere
WHERE (someColumn IN (a,b,c,d,e)
OR someColumn IN (f,g,h,i,j)
OR someColumn IN (.........));
cfqueryparam performs multiple functions.
It verifies the datatype. If you say integer, it makes sure there is an integrer, and if not, it does nto allow it to pass
It separates the data of a SQL script from the executable code (this is where you get protection from SQL injection). Anything passed as a param cannot be executed.
It creates bind variables at the DB engine level to help improve performance.
That is how I understand cfqueryparam to work. Did you look into the option of making several small calls vs one large one?
It is a security issue. Stops SQL injections
Adobe recommends that you use the cfqueryparam tag within every cfquery tag, to help secure your databases from unauthorized users. For more information, see Security Bulletin ASB99-04, "Multiple SQL Statements in Dynamic Queries," at www.adobe.com/devnet/security/security_zone/asb99-04.html, and "Accessing and Retrieving Data" in the ColdFusion Developer's Guide.
The first thing I'd be asking myself is "how the heck did I end up with more than 2100 params in a single query?". Because that in itself should be a very very big red flag to you.
However if you're stuck with that (either due to it being outwith your control, or outwith your motivation levels to address ;-), then I'd consider:
the temporary table idea mentioned earlier
for values over a certain length just chop 'em in half and join 'em back together with a string concatenator, eg:
*
SELECT *
FROM tbl
WHERE col IN ('a', ';DROP DATABAS'+'E all_my_data', 'good', 'etc' [...])
That's a bit grim, but then again your entire query sounds grim, so that might not be such a concern.
param values that are over a certain length or have stop words in them or something. This is also quite a grim suggestion.
SERIOUSLY go back over your requirement and see if there's a way to not need 2100+ params. What is it you're actually needing to do that requires all this???
The problem does not reside with cfqueryparam, but with MsSQL itself :
Every SQL batch has to fit in the Batch Size Limit: 65,536 * Network Packet Size.
Maximum size for a SQL Server Query? IN clause? Is there a Better Approach
And
http://msdn.microsoft.com/en-us/library/ms143432.aspx
The few times that I have come across this problem I have been able to rewrite the query using subselects and/or table joins. I suggest trying to rewrite the query like this in order to avoid the parameter max.
If it is impossible to rewrite (e.g. all of the multiple parameters are coming from an external source) you will need to validate the data yourself. I have used the following regex in order to perform a safe validation:
<cfif ReFindNoCase("[^a-z0-9_\ \,\.]",arguments.InputText) IS NOT 0>
<cfthrow type="Application" message="Invalid characters detected">
</cfif>
The code will force an error if any special character other than a comma, underscore, or period is found in a text string. (You may want to handle the situation cleaner than just throwing an error.) I suggest you modify this as necessary based on the expected or allowed values in the fields you are validating. If you are validating a string of comma separated integers you may switch to use a more limiting regex like "[^0-9\ \,]" which will only allow numbers, commas, and spaces.
This answer will not escape the characters, it will not allow them in the first place. It should be used on any data that you will not use with <cfqueryparam>. Personally, I have only found a need for this when I use a dynamic sort field; not all databases will allow you to use bind variables with the ORDER BY clause.
Given a query (pseudo-code):
<cfquery name="myquery">SELECT * FROM stuff</cfquery>
How do I get rid of the first record? In this case, altering the SQL is not an option.
I have tried: myquery.RemoveRows(0,1); but received an error:
No matching Method/Function for Query.REMOVEROWS(numeric, numeric) found
I'm on Railo 3 BTW
Lo and behold:
myquery.RemoveRow(1);
Does exactly what I wanted. Leave it to Railo to do things a little differently!
Can't think of a way offhand to remove a row from the original object. Two things I can think of are:
do a query of queries. That assumes you'd be able to identify the records you don't want and specify them in the WHERE.
construct a new query with queryNew(). Loop over the original query doing a querySetCell() into the new query for the records that you want. This functionality could be incorporated into a UDF pretty easily. In fact, stating that made me think to check cflib.org. See #3
Check cflib.org :) See http://www.cflib.org/udf/QueryDeleteRows
"RemoveRows" is actually a call to the underlying Java method, so you have to cast those numbers. like so:
myquery.RemoveRows(
JavaCast( "int", 0 ) // starting row, zero-based
,JavaCast( "int", 1 ) // number to delete, returns error if too many
)
So probably the method is "int,int", and if you don't cast it, it looks like "numeric,numeric". One might argue that this is undocumented, but it's so succinct that you could (as I did) wrap it in a function, so that if it changes you just need to replace the contents of the function with an alternative workaround.
Railo has added removeRows, see here. My ACF code that uses this method now runs on Railo too, no changes.
With this, the Railo implementation now matches ACF. (Note also that the original Railo implementation was 0 based, while the new one and the ACF version are both 1 based.)
The way I would normally do it is with a query of queries such as the following:
SELECT * FROM myquery
LIMIT {1,10000}
That should work in Railo. What it does is offset the query by one and pulls 10000 records.
You could also do this:
SELECT * FROM myquery
WHERE {primarykey} <> {value}
Where it selects all records except for the primary key value that you pass in.
The awesome thing about ColdFusion is that there are tons of ways to do exactly what you are looking for.
You could just skip the row when you process the results:
<cfoutput query="myquery">
<cfif myquery.currentrow GT 1>
<!---Do the work here. --->
</cfif>
</cfoutput>