Sunday, July 06, 2008    
Home My Books Blog ColdFusion About Me Back    

Calendar
<< Dec 2005 >>
S M T W T F S
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
             

Search

Categories
 • Adobe (63) [RSS]
 • AdobeMAX06 (45) [RSS]
 • AdobeMAX07 (59) [RSS]
 • AdobeMAX08 (10) [RSS]
 • AIR (108) [RSS]
 • Appearances (112) [RSS]
 • Books (66) [RSS]
 • CFEclipse (14) [RSS]
 • ColdFusion (1105) [RSS]
 • Data Services (6) [RSS]
 • Flash (94) [RSS]
 • Flex (334) [RSS]
 • Jobs (86) [RSS]
 • JRun (12) [RSS]
 • Labs (27) [RSS]
 • LiveCycle (16) [RSS]
 • MAX (145) [RSS]
 • Regular Expressions (12) [RSS]
 • SQL (36) [RSS]
 • Stuff (496) [RSS]
 • Tips (CF Studio) (80) [RSS]
 • Tips (CF) (795) [RSS]
 • Tips (Dreamweaver) (91) [RSS]
 • Tips (Flex Builder) (2) [RSS]
 • Using CF (132) [RSS]
 • Wireless (96) [RSS]

Other BLOGs
 • Ray Camden
 • Tim Buntel
 • Sean Corfield
 • John Dowdell
 • Steven Erat
 • Brandon Purcell
 • Charlie Arehart
 • Full As A Goog

RSS Feeds
 • Feed
 • Subscribe

Join my mailing list and find out about new books and other topics of interest.

Thoughts, ideas, tips, musings, and pontifications (not necessarily in that order) by Ben Forta ...
NOTE: This is my personal blog, and the opinions and statements voiced here are my own.

Viewing By Day : December 21, 2005 / Main
December 21, 2005

SQL Injection Attacks, Easy To Prevent, But Apparently Still Ignored

I was just on a web site (no, not a ColdFusion powered site, and no I will not name names) browsing for specific content. The URLs used typical name=value query string conventions, and so I changed the value to jump to the page I wanted. And I made a typo and added a character to the numeric value. The result? An invalid SQL error message.

That's bad. Very very bad. It means that I was able to create a SQL statement that was submitted to the database for processing, a SQL statement that was passed to the database as is, unchecked.

You'd think that by now we'd have learned to lock down our code so as to prevent SQL injection attacks, but apparently this is not the case. You do not know what a SQL injection attack is? Well, read on.

Consider the following simple dynamic ColdFusion query:

SELECT *
FROM Customers
WHERE CustID=#URL.custid#
Here a WHERE clause is being populated dynamically using a URL parameter. This type of code is common and popular, and is often used in data drill-down interfaces. If the URL was: the resulting SQL statement would be:
SELECT *
FROM Customers
WHERE CustID=100
But what if someone tampered with that URL so that it read: Now the resulting SQL would be:
SELECT *
FROM Customers
WHERE CustID=100;
DELETE Customers
And depending on the DBMS being used, you could end up executing two statements – first the SELECT, and then DELETE Customers (which would promptly delete all data from the Customers table).

Scared? You should be. SQL statements are not just used for queries. They are also used by most DBMSs to create and drop tables, create user logins, change passwords, set security levels, manage scheduled events, even creating and dropping entire databases. And whatever features are supported by your DBMS may be accessible this way.

Before I go further I must point out that this is not a ColdFusion vulnerability at all. In fact, it is not even a bug or a hole. This is truly a feature – many DBMS do indeed allow queries to contain more than a single operation, this is legal and by design.

Of course, you should always be checking parameters anyway before passing them to your DBMS. Passing client supplied data (URL parameters, FORM fields, and even cookies) through unchecked is programmatic suicide. Attacks aside, it is flat out unsafe to ever assume that data submitted by a client can be used as is.

As such, you should already be using code like this:

<cfparam name="URL.CustID" type="integer">
This single line of code will lock SQL injection attacks out. How? Think about it, SQL injection (within ColdFusion apps) is really only an issue with non textual fields. If a text value is tampered with you'll end up with tampered text, but that text will all be part of the core string (within quotes) passed as a value, and will therefore not be executed as separate statements. Numbers, on the other hand, are not enclosed within quotes, and so extraneous text can be tampered with to create an additional SQL statement. And <cfparam> can protect you.

Of course, you may want more control, in which case you could use code like this:

<cfif IsDefined("URL.CustID")
and not IsNumeric(URL.CustID)>

… throw an error or something …
</cfif>

And as an additional line of defense you can use <cfqueryparam>, as seen here:

<cfquery ...>
SELECT *
FROM Customers
WHERE CustID=<cfqueryparam value="#URL.CustID#" cfsqltype="CF_SQL_INTEGER">
</cfquery>
If the previous tampered URL was passed to the this query, the value would be rejected and an error would be thrown. The CFSQLTYPE (aside from binding variables) performs data type validation checks, and values that do not match the type are rejected. That's it, only integers allowed, and malicious tampered URL parameters are not integers.

The bottom line is that SQL injection attacks have been around for as long as dynamic SQL itself. ColdFusion has made it incredibly easy to protect yourself against such attacks. Be it <cfparam> or <cfqueryparam> or your own conditional processing, it's simple to protect yourself, and your responsibility to do so.

If you have not been paying attention to this risk, stop whatever you are doing, fire up your IDE, and do a search for every single <cfquery> in your code. Then quickly scan to find any that contain #'s in them (that are not enclosed in quotes or passed to <cfqueryparam>), and make a list of the variables used. If any of them are URL parameters or FORM fields, create a <cfparam> for each (at the top of the page, or before the <cfquery>). It's that simple. Really. There is no legitimate reason not to protect yourself, so just do it. Now! And I mean right now, before you leave for the day or take off for the holidays, and despite whatever project you are working on or deadline you are up against. No excuses (and if your boss complains about you switching gears to take care of this one, send him my way!).

Enough said! (I hope).

TrackBacks
There are no trackbacks for this entry.

No trackback URL. Trackbacks are only allowed via interactive form.

Comments
Most RSS feeds offer a brief "clipping" (like bloglines.com) and yet yours publishes the whole article. Why is that?
# Posted By Peter Tilbrook | 12/21/05 5:44 PM
Peter, because I never remember to embed <more/> in the body. ;-)
# Posted By Ben Forta | 12/21/05 6:23 PM
Oooookay. So why is that? Too busy? LOL!

Happy holidays!
# Posted By Peter Tilbrook | 12/21/05 6:50 PM
What about query caching using the <cfqueryparam>?
# Posted By ross | 12/21/05 7:26 PM
Ross, as I said, there are multiple solutions. And yes, there are pros and cons to each.
# Posted By Ben Forta | 12/21/05 8:17 PM
>> If any of them are URL parameters or FORM fields,
>> create a <cfparam> for each

Or if there are ANY variables which might have been passed in from one of those scopes.
(eg: <cfparam name="Session.Consumer" default="Url.Consumer"/>)



Personally I'm in the habit of cfqueryparam-ing EVERYTHING with hashes. It's ugly (hopefully CF8 will implement a function version of QueryParam), but it's the simplest way to be sure.

Fortunately I haven't yet encountered any queries that need to be cached, but it's fairly simple to do manually if/when I do.
# Posted By Peter Boughton | 12/22/05 4:53 AM
Also of concern is the ability for a visiter to amend the url from custid=100 to custid=101 to see if they can view another customers details. This can be guarded against by simply making it less obvious and perhaps by building some complexity into the custID - e.g. a checkdigit, or better still remove this from the URL altogether and use a session variable.
# Posted By Mike T | 12/22/05 4:57 AM
We have worked around this by creating custom error results so if anyone else trys to injects something they do not recieve an error that would tell them anything about sql. If they know the table names and such they might be able to do something but since the error messages are no good we they find no info. That being said I am going to start adopting this practice... The more protection the better.
# Posted By Rod Kesselring | 12/23/05 9:32 AM
For an id value, we also do a hashed id checksum value passed as well. That way, if someone modifies the id value, we check hash and check the results against the checksum. If there is a disparity, then we know there is tampering going on and we throw an error and record all the information of the tampering user.
# Posted By Daniel Greenfeld | 12/23/05 9:50 AM
Rod,

Just because I can't see the error does not mean that I can't perform SQL Injection. Google blind SQL injection for lots of info.

The lesson here is to always use parameterized queries (cfqueryparam)! This is the case in all languages including Java, CF, C#, etc. since it is the single best mechanism to preent SQL injection. The secondary and tertiary lines of defense are proper data validation and not sending data to the client that the client shouldn't need to see/modify. Once the client has the data in his or her browser, it's the user's to modify as he wishes!

-dhs
# Posted By Dean H. Saxe | 12/27/05 3:46 PM
For you *nix users, I've made a grep that you can run on your app directories to search for SQL injections.

egrep -ir "(WHERE|AND|OR|[^f]SET) [a-zA-Z0-9\.]+[ ]*=[ ]*[\"']?#" *

It will not match all possible injections, but it will match a lot of them. Also, just want to toss this in, apparently when using cfqueryparam for all variables, depending on your DBMS you can increase performance by a good bit - it already binds variables to their types and can ignore any type checking it has to do on the DB side.

Cheers,
-Umbrae
# Posted By Umbrae | 1/16/06 1:43 PM
Should note, the most important injections it will miss are inserts. But regardless, it still provides for a starting point to see if you've missed any files.
# Posted By Umbrae | 1/16/06 1:45 PM
Would using CFARGUMENT TYPE="numeric" not stop it as well. Assuing your query is in a CFC, and cfc passes in above arg to your query?
# Posted By paul | 1/19/06 10:50 AM
Here's another way to stop injection:

If the value of URL.b = 1,2,3,4,5,

<!--- *** CHECK URL FOR NUMERIC & COMMA USING REGEX --->
<cfset check_url = IsValid("regex",Trim(URL.b),"[0-9,]+")>

This will only allow numbers and a comma for the URL.b variable. You can modify the [0-9,]+ to delete/include other characters.
# Posted By drforbin | 5/2/06 2:30 PM
Wanted to point out that using SELECT * and cfqueryparam together will wreck your query if you add a column to the table your selecting from. Since the DB now caches that query due to the bind parameter, the SELECT gets confused when a new col is added. SELECT * is usually bad practice in general.
# Posted By Daniel | 7/17/06 1:59 PM
just wondering, as this is fairly new to me, is there really any more effective way than cfqueryparam to prevent this? i know that XSS is talked about a lot as being very dangerous, but i just spent the better part of four hours fixing up a site with a lot of where column=#humber# stuff in it.

even if the form was ran from someone's harddrive, there still shouldn't be any danger, right?

i know i sound naive, but i am asking to be sure. thanks much!
# Posted By jason | 1/16/07 12:50 AM
I'm not entirely sure what cfparam does here other than ensures that the URL variable is an integer.

Is there not a was of doing the same for url parameters that are not integers. The reason I ask is I am using a pair of parameters in URLs for user information to ensure that a user cannot simply type in another number and get someone elses detals. one of these parameters is not an integer.

Is there not a UDF or CFC that can check URL and form variables against SQL injection attacks.
# Posted By Keith | 1/22/07 1:50 PM
Keith, CFQUERYPARAM does otherr stuff too, but you are right, as far as preventing SQL injection attacks then it doing just that, ensuring that types arre what they are supposed to be. If you are using your own data types then you'll need to do some testing of your own, thoroughly validate the parameters before using them, ideally saving them to some local variable once validated and cleaned up, and then passing that to the SQL instead of the URL parameter.

--- Ben
# Posted By Ben Forta | 1/24/07 10:06 AM
cfqueryparam is a nice way to prevent sql injection - but our latest version of our website that now uses application.cfc and a bunch of persistent objects has rendered the cfqueryparam tag useless - a nasty bug has arisen that causes CF to return an 'invalid parameter binding(s)' error that Adobe nor anyone else can seem to fix. A perfectly functioning query with validated datatypes being passed into the cfqueryparam tag will randomly throw this error. More strange is that the exception detail shows the datatype of the passed parameter to be consistent with what it is expecting - it isn't a case of defining a cfsqltype that differs from the datatype being passed in. So, unfortunately for us we have to remove all the cfqueryparam tags...
# Posted By Ed Ralph | 2/19/07 4:47 PM
Ben, do you have any thoughts on the comment above by Ed Ralph? We have been seeing this seemingly random and unrepeatable error as well in one of our applications that also uses Application.cfc and big "persistent" (hope Sean doesn't read that) objects. To go a little further, the database interaction is all managed via Reactor, which of course uses validated cfqueryparams as well. I would be *very* interested to hear any thoughts you might have on the matter. Thanks!
# Posted By Dave Shuck | 4/5/07 3:10 PM
Just an update on my previous comment - sorry Ben I know this isn't a forum but...

We went through an exercise of removing cfqueryparams to skirt around the invalid parameter binding(s) error, but now instead we get random sql statement truncation. The simplest (valid) queries are being truncated - or so they appear in the error statements. For example, the statement might end up going to SQL Server like this:
"LECT blah, blah from tblBlah" or "select blah, blah from tblBlah where"

Upon inspecting the code, these queries are perfectly fine... I've heard rumours that CF8 doesn't suffer from these problems, but I haven't been able to get my hands on a beta to play with. As a result, I've had to install BlueDragon 7 (the new one) to see if that works ok. If so, I might have to migrate to BD - a scary prospect.
# Posted By Ed Ralph | 4/10/07 5:15 AM
HI. WHat about if users put SQL markup in the text input area's within a form that is going to end up in db insert. (like this field I'm using to leave a comment in?

Is there a risk there?

Matt
# Posted By Matt | 6/21/07 1:22 AM
Matt - yes, that is what SQL injection typically is, and what this comment thread is largely about.

Regarding my last post (4/10/07) - a hotfix was released for MX 7.02 which has fixed the cfqueryparam issue and some other java.lang.illegalStateException issues.
# Posted By Ed Ralph | 7/31/07 9:04 AM
Wanted to point out that using SELECT * and cfqueryparam together will wreck your query if you add a column to the table your selecting from. Since the DB now caches that query due to the bind parameter, the SELECT gets confused when a new col is added. SELECT * is usually bad practice in general.
# Posted By ?? | 8/30/07 11:08 PM
"the SELECT gets confused when a new col is added. SELECT * is usually bad practice in general. " I not understand you.
# Posted By Kurye | 10/27/07 9:36 AM
What about if users put SQL markup in the text input area's within a form that is going to end up in db insert. (like this field I'm using to leave a comment in?
# Posted By Hotel Bayerischer Wald | 10/27/07 10:23 AM
Hi I am doing a code review of a Cold Fusion application and the developer did not include any cfqueryparam or cfparam tags. The database is Intersystem’s Cache. Does anyone know if the Cache database is vulnerable to SQL injection or does the Cache database not allow multiple SQL statements to be processed?
# Posted By Francis Waldron | 10/30/07 3:14 PM
Should note, the most important injections it will miss are inserts. But regardless, it still provides for a starting point to see if you've missed any files.


http://www.taiyau.com/2007/about_us_e.htm
http://www.taiyau.com/2007/index_e.htm
# Posted By Mini Storage | 12/10/07 9:21 PM
Should note, the most important injections it will miss are inserts. But regardless, it still provides for a starting point to see if you've missed any files...
# Posted By çeviri | 1/9/08 1:11 PM
This can be guarded against by simply making it less obvious and perhaps by building some complexity into the custID - e.g. a checkdigit, or better still remove this from the URL altogether and use a session variable. ?
# Posted By ensest hikayeler | 1/13/08 10:47 AM
Is <cfqueryparam> enough by itself to prevent all sql injection attacks? Or should we be scrubbing our data also? More levels of protection is better but assuming for text data, not numeric data, scrubbing is kind of hard. How would one tell the difference between a real comment about sql vs an injection attack. Does <cfqueryparam> make it so any possible sql in text that's going to used in anything (select/insert/delete/update) be escaped?
# Posted By Caylesin | 1/19/08 5:17 PM
thanks
# Posted By bilgi yar??mas? | 1/29/08 10:19 AM
Matt - yes, that is what SQL injection typically is, and what this comment thread is largely about.
# Posted By gazete | 2/2/08 7:55 PM
What about query caching using the <cfqueryparam>?
# Posted By resim | 2/4/08 2:08 AM
As of CF8, caching works if <cfqueryparam> is used.

--- Ben
# Posted By Ben Forta | 2/6/08 12:57 PM
What about query caching using the <cfqueryparam>?
# Posted By Forum | 2/20/08 5:08 AM
What about query caching using the <cfqueryparam>?
thanks
# Posted By chat | 3/3/08 9:00 PM
thanks and congrats for this info
# Posted By hot pictures | 3/11/08 10:12 AM
"the SELECT gets confused when a new col is added. SELECT * is usually bad practice in general. " I not understand you.
# Posted By canli tv | 4/19/08 4:42 PM
http://tinyurl.com/ozq8x
In case you need another reason to implement this.
# Posted By John Ivanoff | 5/14/08 6:10 PM

  © Copyright 1997-2008 Ben Forta, All Rights Reserved