Checking Facebook's HMAC-SHA256 Encode Signature Using ColdFusion
Thu, May 16, 2013 at 3:46 PM
Paul Cormier in Coding, Coldfusion, Facebook, oAuth

Have you ever had one of the coding problems where the solution comes to you while you're sleeping? I had one of those which I've decided to document because despite extensive searching, I couldn't find anything close to a solution.

The Task Is This

To properly integrate Facebook's Login For The Web to permit your web app users to authenticate themselves in your app using Facebook, I believe that if your app needs any modicum of security, you should also verify the signature on the "Signed Request".

Sounds easy enough, Facebook even provides a PHP example.

With the release of ColdFusion 10, there's even a built-in hmac() function, but it's a bit buggy, and I couldn't get it to work with Railo 4. So I used the HMAC_SHA256() function posted on Summation Technologies' website, which should work across all ColdFusion versions and CFML servers.

Next, I slapped together some code that emulates what the PHP code does. First thing to address is the wonky modified Base64 for URL encoding that Facebook uses. Specifically, you have to swap the '-' and '_' characters back to '+' and '/' characters repectively. 

PHP base64_decode() vs CFML BinaryDecode() & ToBinary()

Regardless of what I tried, I couldn't get Coldfusion to recognize the Facebook Signature as being Base64-encoded:

<cfset binarysig = BinaryDecode( sig, "Base64")>

The error:

Ensure that the input was encoded with the same encoding as the one you are using to decode it with.

The PHP function base64_decode() had no problem at all decoding the string. So, after several hours, I called it a night which is when the solution came to me.

The Solution

I thought to try reversing the process. I took the hashed expected_sig and Base64 encoded it to see what the signature SHOULD look like.

Lo and behold, I got the exact same signature string I'd been trying to decode with one exception: it had an '=' character at the end! From the previously-linked Wiki article:

Some variants allow or require omitting the padding '=' signs to avoid them being confused with field separators.

Apparently, this is one of the "variants" that Facebook uses. The PHP base64_decode() function doesn't appear to have a problem with the missing padding, but the Coldfusion BinaryDecode() function certainly does.

The fix is easy enough, I simply appended a '=' and everything worked! Digging deeper into why the '=' is used at all when Base64-encoding revealed a few extra details which I've implemented in my solution below.

The CFML FacebookSignedRequestIsValid() Function

Here is my only moderately tested function:

<cffunction name="FacebookSignedRequestIsValid" returntype="boolean" access="private" output="false">
    <cfargument name="signedRequest" type="string" required="true" />
    <cfargument name="privateKey" type="string" required="false" default="389659060a49560552d87fd8281257fa" />
	var LOCAL = structnew();
	//Prepare the Signature
	LOCAL.sig = ListGetAt(arguments.signedRequest, 1, ".");
	LOCAL.sig = replace(LOCAL.sig, '-', '+', 'ALL');
	LOCAL.sig = replace(LOCAL.sig, '_', '/', 'ALL');
	//Add correct Base64 padding. See:
	switch (len(LOCAL.sig) mod 3) {
		case 1: {LOCAL.sig = LOCAL.sig & '=='; break;}
		case 2: {LOCAL.sig = LOCAL.sig & '='; break;}
	LOCAL.sig = LOCAL.sig & '='; 
	LOCAL.binarysig = BinaryDecode(LOCAL.sig, "Base64");
	LOCAL.sig = binaryEncode(LOCAL.binarysig, "hex" );
	//Prepare the Expected Signature
	LOCAL.payload = ListGetAt(arguments.signedRequest, 2, ".");
	LOCAL.expectedSig = HMAC_SHA256(data=LOCAL.payload, key=arguments.privateKey); // See:
	//Test if match
	if (LOCAL.sig EQ LOCAL.expectedSig) LOCAL.result = true;
	else LOCAL.result = false;
    <cfreturn LOCAL.result>

This function will likely prove itself useful with other oAuth implementations.

Article originally appeared on (
See website for complete article licensing information.