VSG Developers Blog

ColdFusion, Open Source and the Future of TCFA

In September of 2013 I started a new open source initiative in the ColdFusion community called 'Team CF Advance'. Our goals and our organization were different from previous incarnations of open source teams in the community - with our primary focus being on building open source applications to level the playing field between CFML and other technologies and helping the community engage in open source development with each other to further that ultimate goal.

In the beginning, there was a lot of interest from both the community and the people who literally help shape the language - including Railo and Adobe. Adobe, in particular, was behind this initiative from the start - at least in theory, anyway. And Gert and the Railo guys, Luis and the Ortus guys, Sean and the Blue River guys, etc. were all supportive and offered us as much assistance as they could. All of this helped drive my ambitions and fixed me to my personal goal of helping as many CFML developers in the community as I could by helping to organize, and run, a team of developers dedicated to the cause.

In the very early days there were several people who were instrumental in helping get TCFA off the ground. Steve 'Cutter' Blades, Sean Corfield, Dan Cork, Randy Johnson, Ryan Mueller, Pete Oliver-Kruger and a host of other developers all jumped in with both feet to help launch what did ultimately become one of the largest gatherings of CFML developers into a team of like-minded individuals with some lofty, but realistic, goals. Within the first few weeks, TCFA had an API 'hackathon' run by Dan Cork which gave us some of our first open source code on Github. This was followed shortly by the cfSpeakerList application, written to help the larger Adobe community organize user group and conference speakers in a way nobody else had done. As we became more known, and more aggressive about finding good FOSS projects that hadn't been worked on in awhile, we began to adopt several popular FOSS projects including BlogCFC, CFSelenium and ValidateThis. All the while our repo has continued to grow with various bits and bobs of code our team was writing, and further initiatives were discussed and planned at all levels.

About the only 'complaint' I had, and still have, is the level of participation from the larger team. With 160+ developers, you'd think we would be accomplishing quite a bit, but this has not, yet, turned out to be the case. Aside from the people already mentioned, and a handful of folks not mentioned, very few participated in our efforts - and some that did, sadly, fell short when the word 'test case' was uttered. Needless to say, this was a very discouraging outcome, and all those mentioned tried, in their own ways, to move the rest of the team forward the best we could. Alas, at the end of the day - sign-ups were very high, but participation has been consistently low.

I blame myself, though. I had every intention of being there to keep pushing the community and the ghostly members of our team to jump in and participate, however real life demands changed several months after starting TCFA and I was forced to 'take a break' and rely on other team leaders to take up my slack... which they did beautifully. Upon my return and relieving them of the reins, I started to work up a slew of things we could do as a team to help educate and inspire the non-participating members of the team and get them more engaged. I made good progress towards that end, but sadly real life demands returned to stalk me and TCFA once again fell by the wayside. Since then, I have been working 10-14 hour days with more clients than I can shake a stick at, and while I continue to take bits and bobs of code I'm having to write for clients anyway and making them open source where appropriate - I ran out of time to keep the TCFA fires burning, and the team has suffered as a result.

It is, therefore, with a heavy heart and much frustration that I have decided to step down as 'benevolent leader' of TCFA and turn the reins back over to the leadership team that did such a good job in my previous absence. In particular, Randy Johnson has stepped out from the leadership team to rally the troops, as it were, and try to help get this party started up once again. I cannot thank these guys enough for all of their efforts in the past, and for both understanding my dilemma and volunteering to work together with Randy to keep TCFA alive. While I will remain a part of TCFA, my role will be to help support the leadership team, to continue to write and release FOSS software under the TCFA banner, and to help out where I can for planning and logistics as a part of the steering committee. I will not, however, be returning as 'benevolent leader' at any point in the future - that crown now falls to the remaining members of the leadership team. In either case, it isn't fair to the leadership team, to the members of TCFA or to the CFML community for me to continue on in a role I simply have no time for - so this is the right step for me and everyone else who loves CFML.

I can tell you there are a lot of discussions happening between the leadership team about things we should be doing with TCFA. And I can tell you there is a lot of excitement and a lot of really great ideas coming out that I think the members of our team, and the larger CFML community, will truly enjoy. It is actually quite inspirational to watch the ideas flood forth and know that I'm leaving the reins in very capable hands. TCFA is my baby, after all, and I'm pleased to see that it will continue, and we all hope - flourish, into the future. Stay tuned for more details on these ideas from the leadership team.

I, once again, want to thank the leadership team, Adobe, Railo, Ortus, Blue River and, of course, all the members who signed up and all the members who have participated in discussions and coding with us. I couldn't have pulled this off as well as I did without everyone's support, encouragement, unyielding commitment to the CFML community and the goal of developing FOSS for the betterment of the community. I thank you all - and this is not 'good-bye' - this is just a small change in the larger logistics of the team for the good of the team. :)

If you have any questions, please feel free to post them on the Google groups site, or email me directly.

ColdFusion Web Application Security - Part Five - Encryption

In my fourth article in this series I discussed how you can use obfuscation to further enhance the security of your applications using hashing algorithms. In this article I'm going to build on the last article by adding encryption techniques to the mix. Please be advised that this is a lengthy article.

 

There are a ton of really good articles on encryption available in the wild. Some of them are specific to ColdFusion and some of them are not, but the underlying principles are the same. However, the vast majority of the security related articles I've read fail to demonstrate where (and often how) to implement security best practices using encryption, so this article aims to solve this problem by presenting both the where and the how of encryption, using ColdFusion as the example language, of course.

 

Brief History of Cryptography
Protecting messages (data) from prying eyes has been an ongoing problem since the dawn of mankind - however, history (of encryption) is generally regarded as starting with Caesar - when messages sent through the battle lines had to be encrypted to prevent the enemy from deciphering them if his soldiers were caught and/or killed. The Caesar Cipher, or shift cipher as it has been called, was a fairly simple substitution cipher - it shifted each letter of the alphabet by some fixed number of positions up or down the alphabet - for Ceasar, this number was 3. For example, with a left shift of 5, F would become A and Z would become U, and so on. This method still has a modern implementation in the ROT13 algorithm, where letters are shifted (left or right) by 13 positions. In modern times, the Caesar cipher, and all single alphabet substitution ciphers (including ROT13), are easily broken using frequency analysis and offer absolutely no protection in modern communication. In fact, there is no historical evidence that Caesar's cipher was an adequate solution even in his time, though it is generally believed it was effective as his enemies were largely illiterate or would view the message as being written in a foreign language. It was the Arabs that first broke monoalphabetic and polyalphabetic ciphers around 800 AD and gave the first hints at frequency analysis as a method by which to do this. In the modern age of computers, frequency analysis is used to attempt to discern possible hidden codes in entire volumes of data in everything from the Bible to Moby Dick, so you can imagine that deciphering any messages encrypted with a mono or polyalphabetic technique today would be dead simple.

 

Ciphers and encryption didn't move away from mono and polyalphabetic techniques for a couple of thousand years - cryptography remained largely undeveloped and historically these ciphers were broken more often than not. There are many prime examples of this, most notably during the first world war in which German naval communications were deciphered by British intelligence officers which allowed the British to intercept German naval incursions into the North Sea. During the second world war, the Polish were responsible for breaking the German Enigma cipher, and later initiated French and British intelligence officers as war loomed and rapid advances by the German's to modify the Enigma machine prevented the Polish from reliably deciphering the code themselves. During the second world war ciphers were in use by every involved military with differing rates of success and failure throughout the war. Many of the Japanese naval codes were broken by US intelligence, and vice verse - many of our naval ciphers were broken by both the German and Japanese intelligence officers.

 

Fast forward to the 1970's when, in US history, the DES (Data Encryption Standard) cipher was introduced by researchers at IBM as a way to encrypt sensitive information and was adopted as a standard by NIST as FIPS (Federal Information Processing Standard) 46-3. Unfortunately, due to the key length of DES (56-bits) this standard had to be replaced by 2001 with the AES (Advanced Encryption Standard) for which NIST selected the Rijndael method submitted by two Belgian cryptographers and was announced as FIPS 197. While DES and its slightly more secure cousin Triple-DES are still in use today, its small key size has allowed modern systems to brute force DES decryption in as little as 56 hours (or less, that statistic is a few years old now), marking its continued use as an inappropriate and insecure standard on which to base new cryptographic systems. It is also bad practice to keep data lying around in modern times that is encrypted using DES - Adobe's recent security breach and information disclosure is one very powerful example of this. If you have been using DES for encryption, you are strongly encouraged to change to a better algorithm (discussed later in this article) and decrypt your DES data and re-encrypt it with the stronger algorithm. There are other lessons to be learned from Adobe's folly, also discussed later in this article.

 

Encoding vs Encryption
Many developers have wrongly concluded that encoding their data is as effective, or worse - the same, as encrypting their data. While encoding does have a place in modern cryptographic systems, it is done as an obfuscation step only. Using Base64 to encode your data does make it so it is no longer human readable, but do a Google search for 'Base64 decode' and you'll quickly discover that getting the plain-text of any Base64 encoded data is painfully easy. The same can be said for any other encoding algorithm, such as UU, for example. Mixing of encoding is part of how many encryption techniques work - especially with respect to ColdFusion - but encoding alone is not enough to protect your data from being easily reverted to plain-text. That said, you should consider encoding as an interim step for any encryption schema that involves multiple distinct encryption steps (we'll look at some of these later in the article) as doing so adds to the overall complexity of the end result, and can also offer additional storage options, such as binary instead of text.

 

How To: Encryption
With ColdFusion it is relatively easy to encrypt data. The Encrypt() function quickly takes any plain-text data and encrypts that data with your key, any of a number of different encryption algorithms (platform and version dependent) including DES, Triple-DES (DESEDE) and AES, and a handful of encoding methods including Base64, Hex and UU. For binary data, there is the EncryptBinary() function, which has the same properties as Encrypt().

 

There are, however, a few caveats that simply do not appear in the documentation - though are relatively well covered by other CF developers and in many books on CF - that developers should be aware of. We've covered one of them already in this article - don't use DES. It remains in CF for the purpose of interfacing with older data that is encrypted with it, it is not meant for use in newer development and should be avoided at all costs. It's slightly more secure cousin, Triple-DES (DESEDE) should likewise be avoided, as it is simply a slightly more complex rendition of DES and also easily broken with brute force attacks.

 

Another caveat that doesn't appear in the documentation, but should, is that by default when using a block algorithm (DES, AES) the data is encoded using an ECB (Electronic CodeBook) cipher mode. ECB divides your data into blocks (actually, bytes, but I digress) which are then encrypted separately. This means your data can be easily broken into their respective bit length (bytes size or 'block') and brute force decryption can occur in parallel - increasing the chance that your data will be brute force decrypted. In fact, the larger the data set, the easier it becomes to decrypt the data when using ECB mode. You should instead select CBC (Cipher Block Chaining) mode which feeds the last block of encrypted data as the salt for the next block of encrypted data, preventing parallel brute forcing and making your data that much more difficult to decipher.

 

Here is a basic example of how to use Encrypt() to encrypt some data:


<cfset myPlainText = "This is a block of text we will encrypt" />
<cfset myKey = "VpugAocKZVP8BZfamx96Yw==" />
<cfset myCipherText = Encrypt(myPlainText,myKey,'AES/CBC/PKCS5Padding','HEX') />
<cfoutput>#myCipherText#</cfoutput>

 

In this example, the data you want to encrypt is stored in the variable 'myPlainText', 'myKey' is the value of the encryption key (as returned by GenerateSecretKey('AES')) and 'myCipherText' contains the final result of the encryption in HEX encoded format using the AES algorithm in CBC mode with PKCS5Padding applied. This padding allows the encryption to be padded out to the boundary of the block to further enhance the security of the given data. In this way, encrypting the word 'dog' and the word 'sheepdog' result in different results of the same length (further obfuscating the length of the original plain-text data).

 

How To: Decryption
Decryption is simply a reverse process of the encryption. The only requirements are that you provide the same key, algorithm and encoding to the Decrypt() function (or DecryptBinary() function for binary data) that you provided to the Encrypt() function when you encrypted your data. For example:

 

<cfset myCipherText = "6885DA035E3338C2A49D4D82BBA8D2843DF0B34DD73264D8A8381E6EF8CBBC0207E5
EB9DA20025E8804B81D4FE74DDF6E6365BCFFBD52506D0D992856D7652CC" />
<cfset myKey = "VpugAocKZVP8BZfamx96Yw==" />
<cfset myPlainText = Decrypt(myCipherText,myKey,'AES/CBC/PKCS5Padding','HEX') />
<cfoutput>#myPlainText#</cfoutput>

 

Where To Use Encryption
So, where would you use encryption in your web application? Well, if you read my last article then you probably already have some ideas. Where obfuscation allows you to hash predictable variable, form field, database table and column names, the same cannot be done for the data those variables hold or represent, since hashing is a (more or less) one way encryption and decryption is not feasible - and done correctly, improbable. Instead you would utilize Encrypt() to encrypt the data being sent to your application and then Decrypt() it upon submission. Taking from the example given in my last article, let's look at the following URLs:

 

index.cfm?event=user.edit&userId=5

 

index.cfm/user/edit/userId/5

 

report.cfm?reportId=5&format=pdf&startDate=20130101&endDate=20130912&showCC=true&showAddress=true

 

 
Now let's look at these URLs with obfuscation and encryption:

 

index.cfm?d71dbd73a965c5121d05=5861E3FD39E29DBCBCF2A2066184BF27A56090B4015DCB67FD60C9B934639DFB
.2284C8D149ED0F618551ABD3DBAD362F8818721CF0BB218126DF68BED25C811A&
f37f0e16069831c7b77c=8467A344297B4FDB9D3DDABCB9AFE626AC79FF3B2C8A1681FDE2B68ED15E977C

 

index.cfm/user/edit/f37f0e16069831c7b77c/8467A344297B4FDB9D3DDABCB9AFE626AC79FF3B2C8A1681FDE2B68ED15E977C

 

report.cfm?c18d917f847ed3882833=8467A344297B4FDB9D3DDABCB9AFE626AC79FF3B2C8A1681FDE2B68ED15E977C&cdf629a01b73c8161633=39D439C1A7CCDA4E7DB1CEC00039F57E2ECAA56C53E81025C982B94EE5D73F72&f8fc0b1f964a5b1161e9=DE34861A4EDE1F2EB5602926800AC5B0947F8EBF96D2E5207956220AB1627E9C&dfac6ad78a10b229fd81=37F13AE6424402AEA9D9327196385F23B267D6A64FEFA195D44230257125E45E&cefe388c38588fb775c6= 60B854B58C5D5C763573739F7C0F7F2D22982A30B7F76B2AA18AE38D98EB7547 &af004df650c4b8bcaa82= 60B854B58C5D5C763573739F7C0F7F2D22982A30B7F76B2AA18AE38D98EB7547

 

Now, the URL variable names are hashed (obfuscated) and the URL variable values are encrypted. As you can see, this obscures all the plain-text data that might otherwise divulge information hackers could use to try and figure out how your system works or gain access to data they should not. This also helps prevent variable tampering because without knowledge of the encryption key utilized, or a brute force attack to obtain the key, users would not be able to produce values for those variables that would properly decrypt. This brings up another point about encrypting the values of URL (and form) variables - always wrap your decryption of those variables in a try/catch block. Not only will this help you debug, but can also be used to log activity and flag or prevent further possible variable tampering by that user (by logging and ultimately blocking the user from the system by IP).

 

As with URLs, hidden form values can (and should) also be encrypted for the same reasons I've already stated. For example:

 

<input type="hidden" name="username" value="myUsername" />


VS

<input type="hidden" name="e4db5de9085993fefcac" value="B4410E062DDFED53638CBA708B75B5C23A345EA73E8B649EC1926C56F1F85DD4" />

 

This same principle applies to cookies:

 

<cfcookie name="session_id" value="21453" expires="never" domain=".example.com" httponly="true" preservecase="true" />

 

VS

 

<cfcookie name="cfe5344f93a2764655318" value="7CE0972C1FDA4A91E60CAD388C7F83116A8539F119C591403C0F64CF89CBAF61" expires="never" domain=".example.com" httponly="true" preservecase="true" />

 

For URL, FORM and COOKIE scopes (or any other exposed scope) single pass encryption is probably just fine, especially the URL scope since this has a limited maximum size. For database value storage, however, single pass encryption is simply not enough for sensitive data. Once again I call on Adobe's recent folly where their single pass DES encryption of user passwords was doubly wrong - not only did they use DES but they only encrypted it one time with one key. Tisk, tisk Adobe.

 

Database storage of sensitive data should a) be avoided if at all possible - do you really need all 16 digits of that customers credit card number? and b) utilize a multi-pass encryption schema where the data is encrypted no less than three times using different encryption keys for each pass. This is my personal recommendation, and some may claim it to be overkill, but this is the technique I most frequently use when dealing with storage of personally identifiable or otherwise highly sensitive data and, knock on wood, none of my encrypted data has ever been broken (to my or my clients knowledge).

 

Another mistake Adobe made was to store usernames, passwords and password hint's together in the same table, and to leave some of that information (password hints) as plain-text. I had to do a double-take when I started looking at their leaked database... really, Adobe... you store the password hint with the password, and worse, do it in plain-text? I suppose the developers responsible for this monstrosity didn't realize that users are prone to creating password hints that are tragically easy to guess their password from. Some people even *use their password* as their password hint (which should have never been possible in the first place - you should always ensure that your users do not duplicate their password as their password hint)! Or, how about 'eight ones' as a password hint. Err, so your password is 11111111.

 

The lesson to be learned here is a) don't store anything more in your authentication table than absolutely necessary - userId, username, password. That's it. And b) store related information in a separate table (or more than one) and encrypt all that data using no less than three passes with AES (or better) encryption in CBC mode. I would even go as far as to say that Adobe should have also obfuscated the table and column names of the user table and added random columns with random (encrypted) data to further obfuscate the nature of their users table - at least if the database was leaked then it would have been a tremendous challenge, at worst, and undecipherable at best. 38 million former and current Adobe users agree.

 

Implementing Single-Pass Encryption
Now that we've seen how encryption looks as compared to plain-text data in the URL, FORM and COOKIE scopes, let's take a look at how we would implement this encryption within ColdFusion. Once again working on examples taken from my last article, let's look at implementing encryption with our obfuscation:

 

<cflocation url="index.cfm/user/edit/v#Hash('userId','SHA-256')#/#Encrypt(5,urlKey,'AES/CBC/PKCS5Padding','HEX')#" />

 

Now when we parametrize the values being passed in this URL we have to take into account the encrypted value, as follows:

 


<cfparam name="URL['v' & Hash('userId','SHA-256')]" value="#Encrypt(0,urlKey,'AES/CBC/PKCS5Padding','HEX')#" />
<cfset URL['v' & Hash('userId','SHA-256')] = Decrypt(URL['v' & Hash('userId','SHA-256')],urlKey,'AES/CBC/PKCS5Padding','HEX') />

 

Note that we provide a default value to the URL variable that is an encrypted value - in this case an encrypted zero. I typically have placeholder variables defined as application scoped variables such as APPLICATION.encUrlZero and APPLICATION.encUrlNull to save having to encrypt a zero and space (' ') everywhere it is needed for parameters. Once parameters are defined, you simply set the scoped variable to it's decrypted version (in this case - zero (0) or the appropriate user id). You could also set local or variable scoped variables instead to make the code easier to read and understand, for example:

 


<cfset VARIABLES.userId = Decrypt(URL['v' & Hash('userId','SHA-256')],urlKey,'AES/CBC/PKCS5Padding','HEX') />

 

The same methods would be used to encrypt hidden form values:

 

<input type="text" name="ff#Hash('username','SHA-384')#" value="#Encrypt(myUsername,formKey,'AES/CBC/PKCS5Padding','HEX')#" />

 

To Decrypt our form values, we would simply do the same as we did on the URL:

 

<cfparam name="FORM['ff' & Hash('username','SHA-256')]" value="#Encrypt(' ',formKey,'AES/CBC/PKCS5Padding','HEX')#" />
<cfset FORM['ff' & Hash('username','SHA-256')] = Decrypt(FORM['ff' & Hash('username','SHA-256')],formKey,'AES/CBC/PKCS5Padding','HEX') />

 

And, how did we get our hashed and encrypted cookie?

 

<cfcookie name="c#Hash('session_id','SHA-256')#" value="#Encrypt(thisUserId,cookieKey,'AES/CBC/PKCS5Padding','HEX')#" expires="never" domain=".example.com" httponly="yes" preservecase="yes" />

 

And to decrypt:



<cfparam name="COOKIE['c' & Hash('session_id','SHA-256')]" value="#Encrypt(0,cookieKey,'AES/CBC/PKCS5Padding','HEX')#" />
<cfset COOKIE['c' & Hash('session_id','SHA-256')] = Decrypt(COOKIE['c' & Hash('session_id','SHA-256')],cookieKey,'AES/CBC/PKCS5Padding','HEX') />

 

Variations
One of the cool things about cryptography in general is the variability you can add to the encryption and encoding mix. For the savvy readers you might have already noticed, for example, that all my examples utilize a different key for the URL, FORM and COOKIE scope. This helps prevent information disclosure from URL to FORM scopes. For example, let's say you had a URL that passed in a user id like this using the urlKey as your encryption key:

 

index.cfm?vf7850537c03bc8=C2E8A1C32D87BD43370A0A939C5FB1B9126BAC044C286F04D267A9CF1E2969FD

 

However, if you use a different encryption key in your form, the hidden form value in your form would now be a completely different value:

 

<input type="hidden" name="fff7850537c03bc8" value="E2047873A3D4FC729244FD150803BB1E4B99FDC10ED925721F3AE40710D82857" />

 

This again adds to the complexity of your application and makes both scopes mismatch, from the hackers perspective. Again, security is about layering. Another layer one could add here would be additional encoding, either before the encryption, after the encryption - or both - for example let's look at using the ToBase64() function to further encode our single pass encryption:

 

<cfset myPlainText = "This is a block of text we will encrypt" />
<cfset myKey = "VpugAocKZVP8BZfamx96Yw==" />
<cfset myCipherText = Encrypt(myPlainText,myKey,'AES/CBC/PKCS5Padding','HEX') />
<cfset myBase64Before = Encrypt(ToBase64(myPlainText),myKey,'AES/CBC/PKCS5Padding','HEX') />
<cfset myBase64After = ToBase64(Encrypt(myPlainText,myKey,'AES/CBC/PKCS5Padding','HEX')) />
<cfset myBase64Both = ToBase64(Encrypt(ToBase64(myPlainText),myKey,'AES/CBC/PKCS5Padding','HEX')) />
<cfoutput>#myCipherText#<br />#myBase64Before#<br />#myBase64After#<br />#myBase64Both#</cfoutput>

 

When you run the code above, you'll quickly see that you get very different results back based on how you utilize ToBase64(). Let's add to the complexity - let us use URLEncodedFormat() to further encode the data:

 

<cfset myPlainText = URLEncodedFormat("This is a block of text we will encrypt") />
<cfset myKey = "VpugAocKZVP8BZfamx96Yw==" />
<cfset myCipherText = Encrypt(myPlainText,myKey,'AES/CBC/PKCS5Padding','HEX') />
<cfset myBase64Before = Encrypt(ToBase64(myPlainText),myKey,'AES/CBC/PKCS5Padding','HEX') />
<cfset myBase64After = ToBase64(Encrypt(myPlainText,myKey,'AES/CBC/PKCS5Padding','HEX')) />
<cfset myBase64Both = ToBase64(Encrypt(ToBase64(myPlainText),myKey,'AES/CBC/PKCS5Padding','HEX')) />
<cfoutput>#myCipherText#<br />#myBase64Before#<br />#myBase64After#<br />#myBase64Both#</cfoutput>

 

When you run this code, you'll also quickly see how this changed the output of the encryption dramatically. Decoding the data is done in reverse order. We utilize URLDecode() to decode the URL encoded string we start with. For example:

 

<cfset myCipherText = "602E2F5BADBFE7721EA43501DC657BD3FB5B06DA63B95D6E57C8DF02F9082FD96731
C233F169EF4673B8C9EFA85F43B1CAF9B292FF9FF4A3EEFC0CDFFA3449B18D3BB79F58C144FD556FA38806B7F170" />
<cfset myKey = "VpugAocKZVP8BZfamx96Yw==" />
<cfset myPlainText = URLDecode( Decrypt(myCipherText,myKey,'AES/CBC/PKCS5Padding','HEX') ) />
<cfoutput>#myPlainText#<br /></cfoutput>


<cfset myBase64BeforeText = "CC4CBA51BBFA094FE660A5CB5C5D9CC4474EED10DB94CC1F7AFB7E1B769AB98973B1D3617D115E15969BCD2
2689494D25A17C77E516FC871DFC2CE7DEFC8B9B32A1CEA840B64488AEF6527C1B5939C53908C15DB42028E42B30FEA5AF4968626" />
<cfset myKey = "VpugAocKZVP8BZfamx96Yw==" />
<cfset myPlainText = URLDecode( ToString( ToBinary( Decrypt(myBase64BeforeText,myKey,'AES/CBC/PKCS5Padding','HEX') ) ) ) />
<cfoutput>#myPlainText#<br /></cfoutput>


<cfset myBase64AfterText = "QjQ2NERFQjQxNTQ1MDBDOEQzQUJERTFGNzJDREQ0QkE1Mjg3REM4QjI4MEQxMkNBNDk3RjVBQ0QzRUVCN0EwMjRCRDRFQjNBNUU1Q
jRFOTE1RDE2RjkzOEVBQjIzNEY1M0JFOEQ2NjA4MDgzNkZFRjlFNENBQzI2QTRDM0JGNUFDNDRFRDg3MTAyMzMwMjM0NDNDMDIxNzVGQjJCQkVCRQ==" />
<cfset myKey = "VpugAocKZVP8BZfamx96Yw==" />
<cfset myPlainText = URLDecode( Decrypt( ToString( ToBinary( myBase64AfterText ) ),myKey,'AES/CBC/PKCS5Padding','HEX') ) />
<cfoutput>#myPlainText#<br /></cfoutput>


<cfset myBase64BothText = "ODBDNTRCMkYyOEU2NzZGMjZBNkRCNDIyNzM2QTNEQ0E2NzJGNjYzNTg2QjlDMkZGMDhFQzgwOUM4NjVENDVBRTdGMzdGRkQ1NjI5NjY4NTE2NUV
CODRBRUEwMzRFOTMwMTZEM0IwRTkyOURGQjRERjc3NDAwRTRBOEVGQzFGREE3NjlCNUQ4N0RERDRBNTVFM0EwRDgxMkRCNUE4RTFDNEFCND
dDRDE1RUZFM0I5QjJDOUFEMEE1OEQ2QjI1NTBF" />
<cfset myKey = "VpugAocKZVP8BZfamx96Yw==" />
<cfset myPlainText = URLDecode( ToString( ToBinary( Decrypt( ToString( ToBinary( myBase64BothText ) ),myKey,'AES/CBC/PKCS5Padding','HEX') ) ) ) />
<cfoutput>#myPlainText#<br /></cfoutput>

 

If you copy and paste the above code, first make sure there are no line breaks in the initial <cfset> tag's value (otherwise it will fail), then execute the above and you'll see four lines of 'This is a block of text we will encrypt' on your screen. So, as you can see... mixing encoding with your encryption in various ways can produce various results and should be considered depending on the level of layered security your application needs.

 

Implementing Multi-Pass Encryption
Now that you've seen single-pass encryption in action, you can extend your encryption to multi-pass encryption techniques pretty easily. It is exactly as it sounds - encrypting the data with one key, then encrypting it again with another key then encrypting it again with another key, and so on, up to the level of security you feel comfortable with. For example:

 

<cfset myPlainText = "This is a block of text we will encrypt" />
<cfset myKeyA = "VpugAocKZVP8BZfamx96Yw==" />
<cfset myKeyB = "5+t2z0wcrIQA1ngY+SptiA==" />
<cfset myKeyC = "nbyDHAbVd7phDAsCHaQFRw==" />
<cfset myCipherText = Encrypt(myPlainText,myKeyA,'AES/CBC/PKCS5Padding','HEX') />
<cfset myCipherText = Encrypt(myCipherText,myKeyB,'AES/CBC/PKCS5Padding','Base64') />
<cfset myCipherText = Encrypt(myCipherText,myKeyC,'AES/CBC/PKCS5Padding','HEX') />
<cfoutput>#myCipherText#</cfoutput>

 

Note that not only is this data encrypted three times now, but the second pass uses Base64 encoding instead of Hex. You could use hex for all three, but by modifying the encoding used in the different encryption passes again adds a little to the complexity of your layered encryption security.

 

In today's security landscape - where there seems to be more black hats (including the NSA) than white hats - encryption is all the more important for protecting the identity, personal information and sensitive details about your users, customers, business intelligence, and intellectual property than at any time in the past. Multi-pass encryption is difficult for even the NSA to decrypt without expending large amounts of computing power. Combine some of the encoding techniques used for single-pass encryption with multiple encryption passes and you can easily produce data that is near impossible to break without expending a lot of money and resources. Let's take a look at one of these very secure techniques:

 

<cfset myPlainText = URLEncodedFormat("So sorry NSA - this will take a *lot* of money and time to read if you don't have the keys below!") />
<cfset myKeyA = "VpugAocKZVP8BZfamx96Yw==" />
<cfset myKeyB = "d9Q7BQSj0kSUi8SAk4Yyuw==" />
<cfset myKeyC = "nbyDHAbVd7phDAsCHaQFRw==" />
<cfset myKeyD = "+MMCUM2ky1R5+NLBPMQtGA==" />
<cfset myKeyE = "88aI0pHXxXct764aya7Cag==" />
<cfset myKeyF = "79UX4qPVEVe8EoXPhh0HGA==" />
<cfset myCipherText = ToBase64( Encrypt(myPlainText,myKeyA,'AES/CBC/PKCS5Padding','HEX') ) />
<cfset myCipherText = URLEncodedFormat( Encrypt(ToBase64(myCipherText),myKeyB,'AES/CBC/PKCS5Padding','HEX') ) />
<cfset myCipherText = ToBase64( Encrypt( ToBase64(myCipherText),myKeyC,'BLOWFISH/CBC/PKCS5Padding','Base64') ) />
<cfset myCipherText = ToBase64( Encrypt( URLEncodedFormat(myCipherText),myKeyD,'AES/CBC/PKCS5Padding','HEX') ) />
<cfset myCipherText = Encrypt( ToBase64(myCipherText),myKeyE,'BLOWFISH/CBC/PKCS5Padding','Base64') />
<cfset myCipherText = Encrypt( URLEncodedFormat(myCipherText),myKeyF,'AES/CBC/PKCS5Padding','HEX') />
<cfoutput>#myCipherText#</cfoutput>

 

If you run the above code you'll see that we've quickly created a unique, rather complex, encryption schema that would be difficult for even the NSA to break without the encryption keys used. Lucky for the NSA those keys are provided here for them. So is the routine used. Without these two pieces of information, however, even the NSA would have a hard time deciphering the information encoded in this fashion. It is not impossible, with unlimited money and resources at your disposal, and the NSA counts on that right now, but it would be extremely costly and difficult for anyone, even with unlimited resources, to break this code and decipher the message. For our purposes, we're not trying to encrypt messages that would be of national interest... names, addresses, phone numbers, email, usernames, passwords, password hints and security questions, social security numbers, credit card numbers (neither of the former two should be stored unless *absolutely* necessary), etc. is the data we want to protect from prying eyes. Notice that I've used two different algorithms - AES and BLOWFISH, both in CBC mode, and that I've altered encoding algorithms as well as performed further encoding of data along the six steps taken to create the final result. This adds to the overall complexity of your final output and is again an example (though probably to the extreme) of how to make data highly secure.

 

Key Storage & Retention
There is one last topic I'd like to cover in this article before I let you return to your regularly scheduled programming (ba dump bump) - the topic of key storage and retention. Your data is only as secure as your keys, and your keys are only as secure as your retention policy allows. As such, the keys you use to encrypt should be kept outside of the web root and read into your Application.cfc, should be encrypted by a master key, and should be kept as private as possible from all forms of attack. You should define a key retention and re-encryption policy that forces you to re-key and re-encrypt your data at regular intervals - again defined by the level of security you desire. A typical retention duration is six months to one year. Changing your keys again adds an additional layer of security should your data be compromised (without your knowledge) in the interim between re-keys. If you ever discover your data has been compromised (e.g. someone walks away with a backup of your database like they did with the Adobe hack) then you should re-key your data immediately and probably change the encryption mechanisms as well such that future attacks cannot repeat any decipher discovered in your original disclosure.

 

Defining storage and retention policies is par for the course when encrypting your data. You want to keep your keys private, and change them often enough that you reduce the risk of information disclosure. I store my keys in a binary file, encrypted and encoded, well outside the web root and in a further secured location accessible only by ColdFusion (requires running CF as a dedicated user) and administrators. This helps protect against an outside attacker gaining access to the operating system and obtaining the encryption keys easily. The master key I also store in a file outside the web root, protected by OS methods, and read into the Application.cfc.

 

I think that covers quite a bit of information. I do apologize both for the delay in getting this out and the length of the article, but there is a lot of information to be covered, a lot of which I'm leaving out of this article to cover in another article at a future date perhaps, but I wanted to leave enough room for this post to sink in ;) Thank you for reading and I hope this helps you add layers of security to your web applications and prevent information disclosure.

Formation of OSS CF Development Group: Team CF Advance

In response to Steve "Cutter" Blades What's Wrong With ColdFusion Round 1 Response blog posting I am officially stepping up, again, and encouraging the CF developer community at large to join me and others in forming an official Open Source ColdFusion Development Group to be known as 'Team CF Advance'.

 

The focus of Team CF Advance will be:

 

  • Identify open source software solutions that already exist that can be adapted/modified and adopted by the community as an officially supported solution.
  • Identify open source software solutions that *do not* already exist that can be defined, developed and supported by the community as an officially supported solution.
  • Identify open source software challenges and hackathon's for 3rd party API integrations that can be defined, developed, adapted/modified and adopted by the community as an officially supported solution.
  • Documentation of OSS projects that are generated or improved upon by the team.
  • Promotion and marketing of OSS projects that are generated or improved upon by the team.
  • Speaking on and hands-on classes at user groups and conferences (where available) highlighting the team, our project(s) and teaching how and why to use OSS projects we've developed or improved upon.
  • Advancing ColdFusion applications as an open source solution through development and improvement of open source software solutions combined with social marketing to help raise awareness of ColdFusion solutions as a viable open source option against other open source solutions. .

 

While I plan to help oversee and participate in development projects, I cannot do that alone. I am hoping other leaders in the community will join in assisting me with determining what projects to take on, project management, social marketing, etc.

 

I have set-up a Git account for Team CF Advance and will invite developers interested in joining the team to participate once they have been approved. Developers of all walks of life, skill level and experience are welcome to join the team. The more participants, the better the outcome will be. So, if you have ideas for open source ColdFusion software, want to be part of like-minded teams of developers working towards building quality solutions for the community, enjoy a good challenge, etc. then I encourage you to sign-up for the team today using the link below.

 

If you are interested in joining Team CF Advance, you can use our signup form to sign-up for the team today!

 

NOTE: I will be talking with the guys from the Open CFML Foundation after MuraCon to hammer out the best way to get our projects into the foundation as they meet guidelines.

Railo 4 VirtualBox Developer Image

In addition to the VMWare 9 image I created a couple of weeks ago I have also created this VirtualBox image for those who use VirtualBox instead of VMWare. VirtualBox is an open source virtualization software that is free for download. I'm also working on an image that will work with Vagrant that I will release in the next couple of weeks.

 

CentOS 6 minimal install updated with all the latest security updates as of the date of this posting. This image includes:

  • CentOS 6 x64
  • Tomcat 7
  • Railo 4.0.4 with the 4.1.1 patch installed
  • Apache 2.x
  • MySQL 5.1.x

VirtualBox based image with:

  • 4 processors
  • 2GB RAM
  • Bridged networking
  • Remember to adjust as needed/desired for your environment

The password is the same for all prompts (root, railo admin, mysql, etc.):

 

railodev


 

vsftp is also installed but I have not set up any users to use it as people have different preferences on how they configure FTP access under linux (group shared www dir, upload and copy to www dir, etc.).

 

Firewall has been configured to grant access to port 80, and 21 (you will need to configure port 8888 on this image, I did not open that up).

 

SELinux has been disabled in this version. Being a development image I opted to simply disable it instead of configuring it for web/ftp access this time.

 

This is a minimal install with no desktop support, and I also carved out a little more than I did the VMWare image to get it under 1GB total. I'll revisit the VMWare image later and scale it back some as well so it's not as large.

 

Download Railo 4 VirtualBox CentOS Image (861MB, zip)