Written for OWASP
www.owasp.org
Taco Fleur
ColdFusion security tips
http://www.clickfind.com.au (sponsor of this article)
Wednesday, 23 January 2008
About the author
Taco Fleur is a ColdFusion programmer with approx. 10 years of ColdFusion experience, and is a full supporter of the product. He is currently working for www.clickfind.com.au the Australian business directory/search engine.
Introduction
Over the years ColdFusion has sometimes received a bad name for its security, I truly believe this was due to ColdFusion being such an easy language to pick up and work with. This meant that web designers with no formal programming education could program applications easily and quickly, the downside of that was that some of these applications would be easy to break/hack due to the lack of security knowledge that would have come with a programming degree.
Following are some security tips which I hope will help novices to make ColdFusion applications more secure. And don’t forget, don’t stop with these security tips, they are not meant to be the ultimate protection for your site.
I’ve taken every care possible to be accurate and provide easy to follow examples, if you find any errors, spelling or grammar mistakes, it would be much appreciated if you could let me know on
Please note: this document is currently a work in progress
Index
1. SQL Injection
2. Database Logins
3. Logging
4. XSS (Cross Site Scripting)
5. Cookie Hijacking
6. Proper Error Handling
7. Input Validation
8. Securing Protected Areas
9. Forms being submitted outside of your domain
10. Automated data mining
SQL Injection
What is SQL injection?
SQL Injection is the injection of a malicious SQL statement by a hacker. It transforms your normally safe SQL statement to one that could delete records, drop tables, and much more depending on the rights the user on the ODBC connection has.
Let’s go to a simple example;
<cfquery
name="rsResult"
datasource="myDataSource">
SELECT myColumn
FROM myTable
WHERE myIdentity = #url.myIdentity#
</cfquery>
The above query would get a record from your database based upon the value of url.myIdentity. You could trust the input to always be an integer like; 1,2,3,4,5… 101 etc. but what if someone modified the URL value (which is not hard to do) to 1; DELETE+FROM+myTable
Your previously safe SQL statement would then be injected with “DELETE FROM myTable” and turn into;
<cfquery
name="rsResult"
datasource="myDataSource">
SELECT myColumn
FROM myTable
WHERE myIdentity = 1; DELETE FROM myTable
</cfquery>
When the above SQL statement reaches your database it would first execute
SELECT myColumn
FROM myTable
WHERE myIdentity = 1
and then it would execute “DELETE FROM myTable” which would delete all records from you table.
This is just the tip of the iceberg!
Solution
You should never trust input from a user, and always validate it, we’ll talk more about that later. There are two solutions, and you should probably use both at the same time.
The first is to use <cfqueryparam>
<cfquery
name="rsResult"
datasource="myDataSource">
SELECT myColumn
FROM myTable
WHERE myIdentity = <cfqueryparam value="#url.myIdentity#" cfsqltype="CF_SQL_INTEGER">
</cfquery>
The cfqueryparam tag would check if the value of #url.myIdentity# is actually an integer, as is expected, and if not, it will throw an error. This will prevent the SQL statement from ever reaching the database.
The second step is to convert numeric characters that occur at the beginning of a string to a number with the ColdFusion function val().
<cfscript>
url.myIdentity = val( url.myIdentity );
</cfscript>
Which would turn the URL value “1;DELETE+FROM+myTable” into “1”, thus removing the malicious code, and the query doesn’t throw an error.
You could and probably should use Regular Expression to sanitize the input, but I’m trying to keep this document simple.
Database Logins
It is highly recommended to always allow the minimum rights to a database login. After you have created your database you should create separate logins for several areas and functions. The type of access and functions depends on your database platform, but let’s take a look at the obvious ones.
If parts of your application only SELECTS records from the database, then the only access rights to the login should obviously be SELECT rights. That login should not have access to DELETE, DROP and any others that are not required.
Let’s take for example www.clickfind.com.au, the search functionality only requires the login to have access to SELECT, so we could have created a user called user_frontend and only assign rights to use SELECT to it. This would mean the login would never be able to execute any DROP, INSERT, GRANT or other statements.
To do this you can use your database admin interface, or you can use the ColdFusion administrator and go to Data Sources > Select Data Source > click Show Advanced Settings > and tick the appropriate SQL this login is allowed to execute.
Obviously you would need to modify the rights based on the access required, but always keep it to the bare minimum.
Logging
Logging is mostly overlooked as playing an important part in securing an application; in reality it plays a very important part in security. Without proper logging you might never get to know that someone is executing an automated password attack on your website, trying to find administrator areas, or other holes in your application.
To log 404 errors you should setup a custom error in your web server. This varies from web server to web server.
In IIS you can follow the following steps;
1. Navigate to the website in question under IIS
2. Right click the website and select “Properties”
3. Click the “Home directory” tab
4. Click on “Configuration”
5. Double click the ColdFusion file extension .cfm
6. Check the box “Verify that file exists”
7. Do this for all ColdFusion file extensions
8. Do the same under wildcard application maps
9. Click “OK” when done
10. Click the “Custom errors” tab
11. Scroll down to the 404 error and double click it
12. Change message type to URL
13. Enter /404.cfm
14. And click OK to get out of all the screens
Now we need to create a ColdFusion page that will log all 404 errors to a database and email them. You might think that emailing will fill up your email box, but realistically, your application should not have any 404 errors at all, and therefore you would only be notified about a 404 when someone is trying to do things they are not supposed to, or someone followed an outdated link, in which case you would want to make sure that link get updated.
The ColdFusion page would look something like the following;
<cfheader
statuscode="404"
statustext="Not Found">
<cfmail
to=""
from=""
subject="404 Error"
type="html"
spoolenable="yes"
mailerid="clickfind.com.au">
<p>
404 Error on clickfind.com.au
</p>
<p>
Script name: #cgi.script_name#<br />
Query: #cgi.query_String#<br />
Referer: #cgi.http_referer#<br />
Date Time: #lsDateFormat( now() )# #lsTimeFormat( now() )#<br />
Remote Address: #cgi.remote_addr#<br>
Reverse DNS: #request.rDNS#
</p>
</cfmail>
<cfquery
name="rsInsert"
datasource="myDatasource">
INSERT INTO [error404] (
[url]
, [ipAddress]
, [reverseDNS]
, [userAgent]
, [cookie]
)
VALUES (
<cfqueryparam
value="#listLast( cgi.query_string, ";" )#"
cfsqltype="CF_SQL_VARCHAR">
, <cfqueryparam
value="#cgi.remote_addr#"
cfsqltype="CF_SQL_VARCHAR">
, <cfqueryparam
value="#request.rDNS#"
cfsqltype="CF_SQL_VARCHAR">
, <cfqueryparam
value="#cgi.http_user_agent#"
cfsqltype="CF_SQL_VARCHAR">
, <cfqueryparam
value="#cgi.http_cookie#"
cfsqltype="CF_SQL_VARCHAR">
)
</cfquery>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>404 Error</title>
</head>
<body>
<p>Some text here explaining to the user that they followed an outdated link, and maybe some links to other actions they might want to perform</p>
</body>
</html>
We always want to adhere to the Internet rules, and make sure the search engines find a 404 error when they request an outdated link, which is why we return a 404 error in the header with the <cfheader> tag.
Then we email the error to support @ your domain. I’d like to note that request.rDNS is not a native function of ColdFusion. Getting the reverse DNS gives you more information about the individual who requested the 404, I believe it’s an important piece of information to include, however it requires some custom written classes, which would be to detailed for this article. Please see http://www.google.com.au/search?hl=en&q=coldfusion+reverse+dns&meta= for any resources on reverse DNS in ColdFusion.
And finally, we log the 404 error in the database. If you want to go further and gain more protection, you could log the requests in the application scope and start checking whether more than 60 x 404 requests have been made in a time period of one minute, as that would mean 1 request per each second, and you can be assured that would be an automated attack. Based on this information you could prevent further access to the site.
XSS (Cross Site Scripting)
Cookie Hijacking
I have to say that the jury is still out on the following functionality I came up with. I am pretty sure it protects or at least makes a malicious users’ life more miserable. But I can’t say for sure whether it is fool proof, I’ve been told and I know that an IP can be spoofed, but I do believe that no data can be received back by the user making the request with a hijacked cookie.
If you have any other information or thoughts about this solution, please do let me know on
In your application file you would set a cookie, you would put this in the onSessionStart or alternatively you would it in the application.cfm and first check if it exists or not.
<cfcookie
name="hash"
value="#hash( cgi.remote_addr & application.seed )#"
domain=".clickfind.com.au">
Upon each request you would verify the cookie with
<cfif compareNoCase( cookie.hash, hash( cgi.remote_addr & application.seed ) ) neq 0 >
<!--- perform an action when the cookie is hijacked --->
</cfif>
This should only work with browser cookies, i.e. the ones that get removed when the browser closes. If this was a persistent cookie it would not work. Once again, this is still experimental, come to think about it, I probably should not have included it here yet, but I might as well and get some feedback on it.
Proper Error Handling
You might ask yourself “Why worry about error handling?”, but have you had a proper look at the information that is displayed when a ColdFusion error is thrown? It could give malicious users information like; configuration, the physical location of the application, datasource name, database tables, and maybe even usernames and passwords plus much more information that is very valuable to someone trying to do harm.
On a production machine you should prevent any debugging from being displayed, and to save resources you should probably turn any type of debugging completely off through the ColdFusion administrator panel.
Use an Error handler in your application that catches all errors, notifies you of the error, logs the error and displays a nice message to the user instead of a white page with stuff they cannot understand. Try and keep the message as simple as possible, on www.clickfind.com.au we try and make it a little fun, and the heading of our page is “Oops!”
<h1>Oops, there was a problem!</h1>
<p>
The problem could be caused by a network, database, programming (we're only human) or connection problem, you could try again to see if the problem is solved.
</p>
<p>
The technical team (<a href="mailto:"></a>) has been notified of the problem and will work on fixing this issue as soon as possible.
</p>
<p>
<strong>A reference number for this problem is <strong>[reference number here]</strong></strong>
</p>
<h3>Additional information</h3>
<p>
Following is some additional information that might be helpful.
</p>
<cfif session.User.isSignedIn() >
<p>
You are signed in as #session.User.getScreenName()#
</p>
<cfelse>
<p>
You are not signed in.
</p>
</cfif>
<div id="javascript-enabled">
<p>
You do not have JavaScript enabled.
</p>
</div>
<script language="javascript" type="text/javascript">
document.write( '<p>You have JavaScript enabled.</p>' );
document.getElementById( 'javascript-enabled' ).style.display = 'none';
</script>
<cfif structKeyExists( cookie, "enabled" ) >
<p>
You accept cookies.
</p>
<cfelse>
<p>
You do not accept cookies, you should know that this site requires cookies to be enabled.
</p>
</cfif>
<p>
Your IP address is #cgi.remote_addr#
</p>
<p>
Your reverse DNS is #request.reverseDNS()#
</p>
The above information gives the user confidence that the error is being looked at, and the information displayed would help you debug the error if you were speaking to the client on the phone. It might be that they do not have cookies or JavaScript enabled, the output above would give you that information.
Following will give you some idea on how to setup error handlers in your application.
If you are on ColdFusion MX7 or higher you would use the onError event handler in the application.cfc
<cffunction
name="onError"
returntype="void"
output="true">
<cfargument
name="Exception"
required="true">
<cfargument
name="EventName"
type="string"
required="true">
</cffunction>
Note that the output is set to true, because you will be displaying HTML to the user upon an error.
Between the function tags you would have a cfmail to notify you of the error and email any information about the error itself. You would also include the HTML to display to the user with the friendly error message. And last but not least, make sure we are adhering to the internet rules and return a 500 status error.
<cfheader
statuscode="500"
statustext="Internal Server Error">
In ColdFusion MX6 you would be using the <cferror> tag to include an error template that performs the same actions described above.
Input Validation
You cannot rely on your users to always understand what they need to enter in your form fields, sometimes they make a mistake, don’t understand or are trying to break your system and enter characters that you would not be expecting.
For example, you are expecting a date in your field like “05/11/72” but someone writes “5 Nov 1972”, or a malicious user inputs “<a href=’’ onmouseover=’some malicious JavaScript here’>my name</a>” in a first name field.
Perform client side validation with JavaScript should only be considered a nice feature to make life easier for your users, it should never be seen as a final solution for data validation. JavaScript can be turned off in the browser and bypassed with ease. ColdFusion Server-side validation is the only way to really be sure that you are able to validate the data.
What can happen if you don’t validate your data? You can end up with a messy database full of information that is worth nothing, it can crash your application, or worse, a malicious user could insert nasty tags that could grab people’s cookies and they could hijack their session.