ColdFusion Cfencode and Cfcompile Utilities

ColdFusion has two utilities that are available to obfuscate code. This is useful when you don't want users to see or modify your plain text CFML code. However, there are some drawbacks to both utilities...

I'm interested in doing this for a couple of reasons, the main one being that I don't want our code being distributed freely. Our license agreements physically protect our code from those that adhere to it (which is few). But doesn't physically protect our code from those who don't (which is many). Since our code is not open source and is definitely not free (I don't make money with my good looks), I'm seeing a one to many relationship that needs to be joined with some additional security features.

The other reason is that we're building a license verification system. If we deploy our cfml code, a developer could simply just bypass the verification process. Now this isn't so bad because they couldn't get updates to the code, but is bad because now they have an illegal copy of the system without a valid license key.

SPECS
- Both cfencode and cfcompile are in the [cf root]\bin directory.
- I'm using Windows XP.
- Also using ColdFusion MX 7.

Lets start with cfencode. This utility will encrypt your .cfm and .cfc files from a specific directory. The nice thing about this is that a server admin cannot see your CFML.

For kicks and giggles, here is how the utility works. Open a command prompt and CD to the [cf root]\bin directory. Then enter "cfencode \?" as follows:

cfencode /? command

A dialog box appears explaining the format and parameters for the cfencode utility:

Dialog box from cfencode /? command

OK. So now I'll give it a try:

Results of cfencode. Which is nothing.

Doesn't look like much. But let's compare before and after of my Address.cfc file:

BEFORE

<cfcomponent displayname="Address" hint="Manages the address component ">
<cfset THIS.id = 0>
    <cfset THIS.locator_link_id = 0>
<cfset THIS.line_1 = "">
<cfset THIS.line_2 = "">
<cfset THIS.city = "">
<cfset THIS.state = "">
<cfset THIS.zip = "">
<cfset THIS.is_physical_location = "">
    <cfset THIS.altered = false>

<!--- SET METHODS --->
<cffunction name="set_id">
<cfargument name="value" type="numeric">
         <cfif THIS.id NEQ ARGUMENTS.value><cfset THIS.altered = true></cfif>
         <cfset THIS.id = ARGUMENTS.value>
</cffunction>

<cffunction name="set_locator_link_id">
<cfargument name="value" type="numeric">
         <cfif THIS.locator_link_id NEQ ARGUMENTS.value><cfset THIS.altered = true></cfif>
         <cfset THIS.locator_link_id = ARGUMENTS.value>
</cffunction>

<cffunction name="set_line_1">
<cfargument name="value" type="string">
         <cfif THIS.line_1 NEQ ARGUMENTS.value><cfset THIS.altered = true></cfif>
<cfset THIS.line_1 = ARGUMENTS.value>
</cffunction>

<cffunction name="set_line_2">
<cfargument name="value" type="string">
         <cfif THIS.line_2 NEQ ARGUMENTS.value><cfset THIS.altered = true></cfif>
<cfset THIS.line_2 = ARGUMENTS.value>
</cffunction>

<cffunction name="set_city">
<cfargument name="value" type="string">
         <cfif THIS.city NEQ ARGUMENTS.value><cfset THIS.altered = true></cfif>
<cfset THIS.city = ARGUMENTS.value>
</cffunction>

<cffunction name="set_state">
<cfargument name="value" type="string">
         <cfif THIS.state NEQ ARGUMENTS.value><cfset THIS.altered = true></cfif>
<cfset THIS.state = ARGUMENTS.value >
</cffunction>

<cffunction name="set_zip">
<cfargument name="value" type="string">
         <cfif THIS.zip NEQ ARGUMENTS.value><cfset THIS.altered = true></cfif>
<cfset THIS.zip = ARGUMENTS.value>
</cffunction>

<cffunction name="set_is_physical_location">
<cfargument name="value" type="string">
         <cfif THIS.is_physical_location NEQ ARGUMENTS.value><cfset THIS.altered = true></cfif>
<cfset THIS.is_physical_location = ARGUMENTS.value>
</cffunction>

    <cffunction name="set_altered">
       <cfargument name="value" type="boolean">
      <cfset THIS.altered = ARGUMENTS.value>
    </cffunction>

<!--Get Methods for the Address-->
<cffunction name="get_id">               <cfreturn THIS.id>                  </cffunction>
<cffunction name="get_locator_link_id">      <cfreturn THIS.locator_link_id>         </cffunction>
    <cffunction name="get_line_1">            <cfreturn THIS.line_1>               </cffunction>
<cffunction name="get_line_2">            <cfreturn THIS.line_2>               </cffunction>
<cffunction name="get_city">               <cfreturn THIS.city>               </cffunction>
<cffunction name="get_state">               <cfreturn THIS.state>               </cffunction>
<cffunction name="get_zip">               <cfreturn THIS.zip>                  </cffunction>
<cffunction name="get_is_physical_location">   <cfreturn THIS.is_physical_location>   </cffunction>
   
    <cffunction name="commit_db" access="public">
          <cfif THIS.altered>
             <cfquery name="commit_address" datasource="#application.DSN#">
               SELECT public.sp_commit_address
               (
                  <cfqueryparam cfsqltype="CF_SQL_INTEGER" value="#THIS.id#">,
                  <cfqueryparam cfsqltype="CF_SQL_INTEGER" value="#THIS.locator_link_id#">,
                  <cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#THIS.line_1#">,
                  <cfif Trim(THIS.line_2) EQ "">
                     <cfqueryparam cfsqltype="CF_SQL_VARCHAR" null="Yes">,
                  <cfelse>
                     <cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#THIS.line_2#">,
                  </cfif>
                  <cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#THIS.city#">,
                  <cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#UCase(THIS.state)#">,
                  <cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#THIS.zip#">,
                  <cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#THIS.is_physical_location#">
               )
            </cfquery>
            <cfset THIS.altered = false>
            <cfset THIS.id = commit_address.sp_commit_address>
         </cfif>

         <cfreturn THIS.id>
    </cffunction>

</cfcomponent>

AFTER

Allaire Cold Fusion Template
Header Size: New Version"?Ä!þ
æ‚ÆÔíG¿hqؤä8X°É¿Ìò©‰P^qv ßNÊÒ‡ùFÍû'ÉÊËåwI©Mé›UM§7/;<ôéêt1ÈÖ8&á§õƒ.T–¢ ‰Ö–   žŒ¾nš@0„oèK?œa_òiÙ68#›|_á¡zÒ¶BαïÔ„üŸ©´©zl¶ì4h·'‹–/y?¥ãlÚH
7õò·¢*ÕÀ€°ûé{'P_í? Ý­Lø8b䟺Xõ`-öÁ<>˜>,ÉÒ vg+¡‡¦%ß"«/span>Sª¼--uÄ®2 ˆ,ÉÒ vg+¤ÁÏ!JB[X¦œH#ÃA'‡g2?Á-öÁ<>˜>,ÉÒ vg+ÉÌRMo[ýÕ‹º)?Ú#-öÁ«>˜>,ÉÒ vg+ÉÌRMo[ý±á*ðÝÚ†-öÁ«>˜>,ÉÒ vg+Â3¸¡Iê=eÌ©z†4é }5¸«span style="color: #0000ff">"5ÑTÄ[ƒQrAk~%®H>NŒå©ä^ã/â®ýp-F×·Ìê¿$äÖ?RÃ ÔÆå0?µìPÝÃqß6{Λð/NÊ3£tTÓñeS"

Nifty, eh? Now you can release your code to anyone and they won't be able to modify it, right? WRONG! Adobe (or a guy named Gary Martinez who built the code and posted it on Adobe) has conveniently provided a free utility to decrypt encrypted CFML pages: http://www.adobe.com/cfusion/exchange/index.cfm?event=extensionDetail&extid=1007043

Also, word is that the algorithm used to encrypt the cfml pages is quite easy to crack. Thus, you can also search google for other programs that do the same thing, ranging in price from free - $75.

Nice eh? So, your code is really not that safe after you cfencode it. Just thought I'd mention that.

Now, another word is that you can google other encrypt utilities that will safely encrypt your code and its difficult to crack. You usually have to pay for these tools.

On to the next utility: cfcompile.

Most CF developers know that CFML is compiled down to Java bytecode and then executed through the built-in JRun or J2EE server. CFEncode allows you to deploy your application as Java bytecode, thus obfuscating it to prevent prying eyes.

Contrary to the cfencode utility, cfcompile has plenty of Adobe documentation: http://livedocs.adobe.com/coldfusion/7/htmldocs/wwhelp/wwhimpl/common/html/wwhelp.htm?context=ColdFusion_Documentation&file=00001762.htm

Like cfencode, cfcompile allows you to deploy "sourcelessly". It does this by compiling your CFML into Java bytecode, which is very obfuscted. However, unlike cfencode, cfcompile is a little bit more difficult to retrieve the original CFML. In fact, I have not been able to find a utility or figure out a way to retrieve the orginal CFML. Which is a big plus for us. But that doesn't mean it does not exist.

So, here is how it works. Open a command prompt and cd to the [cf root]\bin directory. The format and paramters for cfcompile are found in the documentation linked above. Because I'm deploying my application, I use the -deploy argument as follows:

using cfcompile

The first argument "-deploy" simply tells the compiler to send the compiled files to a directory. The second argument is the location of the ColdFusion web root. Third is the directory I want compiled, and finally the directory I want the compiled files saved to.

Executing this command produces a lot of output which is each file being compiled to Java bytecode:

output from cfcompile

It takes a few minutes depending on how many files you have in the directories. In the end you will see:

The results of cfcompile

Any errors usually mean there is a CFML problem that needs to be fixed prior to deploying. As you can see I have one error.

Let's take a look at the code:

BEFORE

AFTER

compiled code

The compiled code actually runs just like the the original application (I have not found any issues yet). So, now your code is safe from prying eyes, right? Not necessarily. The compiled code can be decompiled into Java. Here is what it looks like after that process:

After that, I cannot find any way to "decompile" back to CFML. Which might be a little better than cfencode because, it would probably take a lot of effort and work to move around the java class to figure out what the crap is going on. Anyway, it is now possible to modify your application, but in a very roundabout way.

Now we've got to decide if there are any better alternatives to keep the code from being modified? Heck, maybe we'll build our own. That would be cool.

Comments
Contact Chris SchofieldBlogCFC was created by Raymond Camden. This blog is running version 5.9.001.