Home > ColdFusion > Automatically encrypting and decrypting data with ColdFusion ORM and annotations

Automatically encrypting and decrypting data with ColdFusion ORM and annotations

I’m writing an application that uses ColdFusion’s ORM features heavily. Various fields in my database deal with Personally Identifiable Information (PII) and need to be encrypted to meet regulatory requirements. I’ve been mulling over the best way to deal with keeping the data encrypted while in the database but have it be readily usable when loaded into an entity. None of the scenarios that came to mind felt right or could be implemented without an extensive amount of “work-around” code and I just wasn’t willing to go down those paths.

I put the question out to the folks that follow me on Twitter and got a couple of responses, one of which was from Mark Mandel who suggested using annotations. Now, I’ve heard the word annotation mentioned, but had never had the time/opportunity to research what they were or how they were used. That was a few weeks ago and in the meantime, I’d gotten busy focusing on other things and just got around to thinking about the encryption thing again a few days ago. What I found out astonished me.

In ColdFusion CFCs, an annotation is nothing more than an additional attribute you put on a component, property or function tag (and maybe others too, but these will do for my scenario). To test this theory, I added the attribute encrypted=”true” to the password property of my User entity (the user accounts for people that are authorized to log into and use the application) and issued an ormReload() command to recreate the ORM entities. That worked without an error, but at this point it really didn’t do anything except just add a bit of metadata to that property tag.

The secret to the automatic encryption and decryption comes when you combine the encrypted=”true” annotation with the event handling methods provided by ColdFusion ORM. ORM provides the ability to run your own custom routines at various points throughout the ORM request cycle–preInsert(), preUpdate() and postLoad() are three of the ones that I took advantage of. In essence, I used the preInsert() and preUpdate() methods to automatically replace the clear-text value of properties that had the encrypted=”true” flag with an encrypted version of that value. Similarly, I used the postLoad() method to reverse the process and replace the encrypted string that is stored in the database with the clear-text value.

I’ve included the code bits that I used below.

The password property of the User entity:

1
2
3
4
5
6
<cfproperty name="password" 
     encrypted="true" 
     type="string" 
     length="100" 
     required="true" 
     notnull="true" />

The ORM event handler methods:

1
2
3
4
5
6
7
8
9
10
11
<cffunction name="preInsert" output="false" access="public" returntype="void">
     <cfset encryptProperties() />
</cffunction>
 
<cffunction name="preUpdate" output="false" access="public" returntype="void">
     <cfset encryptProperties() />
</cffunction>
 
<cffunction name="postLoad" output="false" access="public" returntype="void">
     <cfset decryptProperties() />
</cffunction>

I then created the encryptProperties() and decryptProperties() methods to loop through the properties of the given object looking for any properties that had the encrypted=”true” attribute set and perform the associated transformation when/if one is found:

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
<cffunction name="encryptProperties" 
     output="false" 
     access="public" 
     returntype="void" >
 
     <cfset var props = getMetaData( this ).properties />
 
     <cfloop array="#props#" index="prop">
          <cfif structKeyExists(  prop, "encrypted" ) AND prop.encrypted >
               <cfset variables[ prop.name ] = 
                 encrypt( variables[ prop.name ], "<key>", "AES" ) />
          </cfif>
     </cfloop>
 
</cffunction>
 
<cffunction name="decryptProperties" 
     output="false" 
     access="public" 
     returntype="void">
 
     <cfset var props = getMetaData( this ).properties />
 
     <cfloop array="#props#" index="prop">
          <cfif structKeyExists(  prop, "encrypted" ) AND prop.encrypted >
               <cfset variables[ prop.name ] = 
                 decrypt( variables[ prop.name ], "<key>", "AES" ) />
          </cfif>
     </cfloop>
 
</cffunction>

To make the above approach even more useful, I put all those methods in a CFC named AbstractEntity.cfc that all my ORM entity CFCs extend. So, now whenever I need to add another column to store encrypted data (no matter which entity I need them in as long as that entity extends the AbstractEntity.cfc), I define all the attributes for the property as normal and simply add the encrypted=”true” attribute to the property and ColdFusion handles the encryption and decryption for me automatically. The end result is that not only does this happen transparently to me as the developer, the clear-text values that I need to interact with in my application are encrypted when stored in the database as well as while in transit to/from the database.

Categories: ColdFusion Tags:
  1. Justin Scott
    April 4th, 2011 at 11:52 | #1

    I was not aware of those “extra” methods that could be defined for ORM CFCs. Obviously I have some catch-up reading to do in the CF9 documentation. Thanks for pointing those out as I imagine they will be very useful.

  2. April 4th, 2011 at 12:16 | #2

    @Justin – There hasn’t been a lot of information that I’ve seen detailing use cases for the ORM event handlers. Adobe’s documentation talks a little about them–Event handling in a persistent CFC lists the ORM event handlers that are available.

  3. Don Blaire
    April 4th, 2011 at 17:15 | #3

    Thanks for sharing. I was looking for something like this to interact with secure data.

  4. April 8th, 2011 at 16:08 | #4

    Nice post Dan. A gotcha to look out for is a bug in CF9.01, where preUpdate fires twice. I blogged about it here:
    http://www.aliaspooryorik.com/blog/index.cfm/e/posts.details/post/cf9-01-preupdate-fires-twice-278

    It has subsequently been patched in Cumulative Hotfix 1 (CHF1):
    http://kb2.adobe.com/cps/862/cpsid_86263.html

  5. April 8th, 2011 at 16:18 | #5

    Thanks John. That bug in 9.01 makes me really glad I kept up with applying the hotfixes.

  6. March 27th, 2012 at 03:47 | #6

    Hi Dan, nice post. One thing, this makes the object dirty the moment it is fetched. Any suggestions on how to prohibit this, or do you think this is not an issue?

  7. March 27th, 2012 at 09:06 | #7

    I hadn’t thought of the dirty object angle because honestly I’m not using a “dirty check” to determine whether or not to save the object. The only way I could see solving the dirty issue is to figure out how to do the encryption/decryption on the database side. I think that’s technically possible with some database systems, but it’s not something I wanted to pursue on my end.

  8. Jonathan Price
    September 16th, 2013 at 15:18 | #8

    Dan, thanks for the idea! Have any of you tried it with subclass extensions? I’ve got a Patient class that extends a Contact class (which extends an Entity class containing these functions). When I load a Patient, I’m seeing the Patient fields with encryption annotations being decrypted correctly, but any fields in the Contact class marked for encryption are not. Any suggestions? Again, thanks for the tip.

  1. No trackbacks yet.
*