ColdFusion 10 Beta - Generating Hash-Based Message Authentication Codes With Hmac()
We are entering the age of APIs - Application Programming Interface(s). Many of our applications now interface with 3rd party APIs like Twilio, Twitter, EmailYak, FullContact, Face.com, and Facebook. The list goes on and on. The available functionality is simply mind-blowing; unfortunately, when we interact with 3rd party services, we do open ourselves up to another attack surface. Luckily, we can use shared private keys as a way to create Hash-based Message Authentication Codes - or, HMAC values - in order to authenticate the senders and recipients of messages. This approach used to be a rather trying feature to implement; with ColdFusion 10, however, generating HMAC values is now incredibly simple.
I don't fully understand the mechanics of generating a Hash-based Message Authentication Code. I can tell you that it works by creating a message digest with the aide of a given secure hashing algorithm. EmailYak uses the MD5 hashing algorithm; Pusher uses the Sha-256 hashing algorithm; and Twilio uses the Sha-1 hashing algorithm. In Java (and therefore in ColdFusion), the application of the given hashing algorithm is encapsulated within the classes javax.crypto.spec.SecretKeySpec and javax.crypto.Mac. And still, creating hash-based message authentication codes requires a lot of work.
In ColdFusion 10, we now have the function hmac(). hmac() fully encapsulates the selection and application of the hashing algorithm, the creation of the message digest, and the conversion of the digest into HEX format. In other words, hmac() took a complex algorithm and turned into a single function call.
To see the huge difference this has made, take a look at the following code. In this demo, I'll create an MD5-based message authentication code (HMAC) using both a custom-built, user defined ColdFusion function and the new hmac() function. The difference will become obvious.
<cfscript>
// I take an API key and a content value and generate a hashed-
// message authenticate code using MD5 so as to be able to
// authenticate that the message is from a trusted source.
function md5Digest( content, apiKey ){
// We need to hash the content using the MD5 algorithm. Let's
// define a key specification for the HmacMD5 alrorithm using
// our API key.
var secretKeySpec = createObject( "java", "javax.crypto.spec.SecretKeySpec" ).init(
toBinary( toBase64( apiKey ) ),
javaCast( "string", "HmacMD5" )
);
// Now, let's create our MAC (Message Authentication Code)
// generator to hash the actual email post content.
var mac = createObject( "java", "javax.crypto.Mac" ).getInstance(
javaCast( "string", "HmacMD5" )
);
// Initialize the MAC using our secret key.
mac.init( secretKeySpec );
// Hash the content of the message - returnes byte array.
var hashedBytes = mac.doFinal(
toBinary( toBase64( content ) )
);
// Now that we have our hashed bytes, we need to encode them
// as a Hexadecimal string. Create a buffer to hold the hex
// values as we encode each byte.
var hexBuffer = [];
// Loop over the bytes to encode them individually as HEX.
for (var byte in hashedBytes){
// Get the hex value for this byte. When converting the
// byte, only use the right-most 8 bits - the sign of
// the byte can create oddities otherwise.
var hexValue = formatBaseN( bitAnd( 255, byte ), 16 );
// When appending the HEX value, ensure that the leading
// zero has not been trimmed during the conversion.
arrayAppend(
hexBuffer,
right( "0#hexValue#", 2 )
);
}
// Flatten and return the Hex buffer.
return(
ucase( arrayToList( hexBuffer, "" ) )
);
}
// ------------------------------------------------------ //
// ------------------------------------------------------ //
// Set up our security key and our message to authenticate.
apiKey = "icanhazsecyouritea";
message = "The content to be authenticed using message digest!";
// Get HMAC (hashed-message authentication code) using the manual
// algorithm and hex conversion.
writeOutput(
md5Digest( message, apiKey )
);
writeOutput( "<br />" );
// Use new built-in Hmac() method.
writeOutput(
hmac( message, apiKey, "HmacMD5" )
);
</cfscript>
As you can see, the 50-line user-defined function is replaced with a single call to hmac(). And, when we run the above code, we can see that the two approaches generate the same hash-based message authentication code (HMAC):
51A2E5E7AA08926C53AAC45356ABA0C9
51A2E5E7AA08926C53AAC45356ABA0C9
The new hmac() function is so much easier!
As dealing with 3rd-party APIs becomes commonplace, message authentication becomes a fact of life. Not only do we have to authenticate where incoming messages came from, 3rd-party APIs are requiring that we authenticate who we are (when we send messages out). Many sites are using hash-based message authentication codes (HMAC) as a way to perform this security check; while this was possible before ColdFusion 10, the new beta makes this security handshake incredibly simple.
Want to use code from this post? Check out the license.
Reader Comments
Sixteen days have passed and no one has made your day yet? This is a good post! How can I have been the one to make your day? Seriously?!
@Randall,
I guess not many people were as excited about the new hmac() method as me :D I appreciate you making my day!
This is awesome! CF10 is going to rock
I was reading the CF10 docs and for "algorithm" & "Encoding" it doesn't offer a list of allowable options.
http://help.adobe.com/en_US/ColdFusion/10.0/CFMLRef/WS932f2e4c7c04df8f744b691e1353e37d519-8000.html
Any idea regarding all of the algorithms & encoding that Hmac() supports? (Will this be further documented?)
On another note (hash related) I recently read an article recommending the use of bcrypt or BKDF2 "exclusively" to hash anything that needs to be secured:
http://www.codinghorror.com/blog/2012/04/speed-hashing.html
Here's an article on how to integrate bcrypt w/ColdFusion:
http://blog.mxunit.org/2011/02/hashing-passwords-with-bcrypt-in.html
bcrypt looks pretty nice & I was wondering if any additional security-related features, like bcrypt, may make it into CF10?
@James,
I know there are licensing issues between some of the various encryption algorithms. There are some that can only be accessed via Enterprise licensing. If you look up the documentation for the encrypt() function, you can see the licensing differences in algorithm support between Standard and Enterprise.
I don't know if that applies to this particular function, or what sub-set of algorithms this supports.
I can say that in my [limited] experience, all the hashing algorithms I've come across when dealing with 3rd party APIs was MD5, Sha-1, and Sha-256.
I don't know too much about bcrypt. Honest, I don't know too much about advanced security and cryptography. I'll have to check out Marc's article on the matter, thanks!