ColdFusion Security - Detecting Modified Pages

No matter how secure your server is there exists the potential for someone to upload their own pages or modify existing pages on the server via an exploit of some sort. How can you detect this or stop this from happening?

A little ColdFusion code can help detect modified or unknown pages and stop these pages from running.

As well as hashing passwords (see previous article on securing passwords) you can also take hashes of pages to check if they have been modified.

First we need to generate the hash of the pages in our site we want to make secure. This can be done by using cffile to read the ColdFusion files and generate a hash for each page via the hash function. The page path and the page's hash are then stored in a database table. (If the site has a small number of pages they instead could be stored in a structure in the application scope).

We then add an onRequest method of Application.cfc or code to the Application.cfm page to check the hash of the current page with the hash of the page stored in the database. If the hashes are the same then the page runs as normal. Otherwise the page has been modified or a new page and the page isn't run but an error page is displayed instead. The error page is a normal ColdFusion page so could log or email the error.

Here's what the code in Application.cfm would look like:

view plain print about
1<!--- read the cfm file    --->
2<cfset pagecontents = "">
3<cftry>
4    <cffile action="read" variable="pagecontents" file="#GetBaseTemplatePath()#">
5    
6    <cfcatch>
7    <!--- if error reading file continue --->
8    </cfcatch>
9</cftry>
10
11<!--- get page from database --->
12<cfquery name="dbpage" datasource="#dsn#" >
13    select page, hash from pages
14    where page = <cfqueryparam value="#CGI.SCRIPT_NAME#" cfsqltype="cf_sql_varchar">
15</cfquery>
16        
17<!--- check if page exists and page hash is correct --->
18<cfif dbpage.recordcount is 0 or hash(pagecontents) neq dbpage.hash>
19    <cfinclude template="error.cfm">
20    <cfabort>
21</cfif>

Or the onRequest method of Application.cfc:

view plain print about
1<cffunction name="onRequest" returnType="void">
2        <cfargument name="page" type="String" required="true">
3    
4        <!--- read the cfm file     --->
5        <cfset var pagecontents = "">
6        <cftry>
7            <cffile action="read" variable="pagecontents" file="#page#">
8            
9            <cfcatch>
10            <!--- if error reading file continue --->
11            </cfcatch>
12        </cftry>
13        
14        <cfset var dbpage = "">
15        
16        <!--- get page from database --->
17        <cfquery name="dbpage" datasource="#variables.dsn#" >
18            select page, hash from pages
19            where page = <cfqueryparam value="#page#" cfsqltype="cf_sql_varchar">
20        </cfquery>
21                
22        <!--- check if page exists and page hash is correct --->
23        <cfif dbpage.recordcount is 1 and hash(pagecontents) is dbpage.hash>
24             <cfinclude template="#arguments.page#">
25        <cfelse>
26            <cfinclude template="error.cfm">
27        </cfif>
28    </cffunction>

Either way I'd recommended (if possible) that the Application.cfm or Application.cfc is kept outside of the web root.

Of course the down side of doing this is that ever time you make a change to a file you need to change the page's hash in the database so it can run. There is also a small performance hit of reading the file and generating the hash on every request.

The above code only checks requested pages but it wouldn't take too much work to have instances of CFCs or custom tags checking themselves as well.

TweetBacks
Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Instead of holding the pages on the server, why not just pull the content from the database?

You can have a SELECT only datasource on the site, then nobody will be able to update your files anyway.

If they're capable of putting content on your site then surely they can replace the application.cfm as well, which makes this method null and void.
# Posted By Phil Arnold | 4/25/07 8:40 PM
Even if you do pull the content from a database you still need a page display the database content and that page could be modified.

Yes worse case Application.cfc or Application.cfm could be replaced that's why I suggested it should be placed outside the web root to reduce the chance of this happening.

Could someone still alter files on your server with this system in place? Yes they could but a) some protection is better than none and b) it would be more effort for them to do so.
# Posted By Justin Mclean | 4/26/07 12:00 AM
Suppose the Application.cfm/cfc is placed outside the web application root as reccommended. Could an attacker place an application.cfm in the app's root, effectivley "blocking" the original application file?
# Posted By Christian Ready | 4/26/07 2:22 AM
I don't see how this is much of a benefit of security. If someone is able to modify your pages via an exploit of some sorts, what prevents them from modifying your Application.cfm or Application.cfc and removing this security code?

Another reason I find this method obsolete is that this method will add mode overhead to your database. You probably won't see it on the a site that gets 5K hits a day, but on a site like mine that get 4.5 million hits a day, I think this method will melt my database server.

In the end, I think the best method to go to protect your website from hackers is the old fashion ways. Make sure you have all ports disabled accept port 80. Use a firewall and NAT the incoming requests to the webserver so you don't have to the server on a DMZ. Have another internal firewall from the webserver to the DB server and only open the ports necessary to access the DB server. Limit the CF services by using a user account and granting permission where needed. Disable FTP access completely or if you need it, use SFTP instead and limit by IP address and port.
# Posted By tony petruzzi | 4/26/07 3:15 AM
Christian. You could place an Application.cfm in the applications root that includes the one outside the root.
# Posted By Justin Mclean | 4/26/07 7:46 AM
Tony. I wouldn't see this as the only way to protect a site just an additional way esp if you need some confidence that a page hadn't been altered. Yes someone could modify Application.cfc or Application.cfm but that's unlikely to be the first target as as I said above this could also check itself.

The database load is tiny and likely to be less than other database calls on the page for your typical dynamic ColdFusion page. The page query could be cached if performance was an issue. With a site with large amount of traffic you would want to load test this before implementing it in production.
# Posted By Justin Mclean | 4/26/07 7:58 AM