Wednesday, May 17, 2017

Passing Encrypted Data Between PHP and ColdFusion

During a recent project at work, I came across the need to pass an identifier between a page written in PHP and a web form developed in ColdFusion. The identifier is the user's unique key in our database, and while it's not a super-top-secret piece of information, it's also something we did not want directly exposed in the browser's URL in this case.

The solution turned out to be rather simple. We decided to encrypt the identifier, and pass the encrypted string in the URL. The following examples will encrypt a string in PHP and decrypt it in ColdFusion, although the process can be reveresed to pass data from ColdFusion to PHP without much trouble. You need three things to be hard-coded at each end of this process:
  1. The IV (initialization vector)
  2. The encryption key
  3. The encryption cipher

Encrypting a string in PHP


The PHP side is fairly straightforward. PHP needs to be compiled with OpenSSL support, and then you simply use the openssl_encrypt() function to generate the encrypted string.

encrypt.php
<?php
    $raw    = "This is a test!";   // The string to be encrypted
    $iv     = "3a513d6df56cd0fd";  // The initialization vector
    $key    = "b341813267566422";  // The encryption key
    $cipher = "AES-128-CBC";       // The encryption cipher

    // Generate the encrypted string
    $enc = openssl_encrypt($raw, $cipher, $key, 0, $iv);

    if( $enc === FALSE ) {
        die("Encrypt operation failed.\n");
    }

    // Generate the URL with the encrypted string as a parameter called 'e'
    $url = "https://www.example.com/decrypt.cfm?e=" . rawurlencode($enc);
    print "$url";
?>

In this example, $raw is the string to be encrypted, and I have hard-coded it for simplicity. It can actually be any string, such as POST data from a form, a value from a database, etc.

Decrypting a string in ColdFusion


The ColdFusion side is a bit more complicated, but still not too bad. ColdFusion supports encryption and decryption by default. You use the decrypt() function to decrypt the string, but first you have to convert the input vector into a binary object, the encryption key into a Base64-encoded string, and sanitize the string passed in the URL.

decrypt.cfm
<!--- Convert the IV to a usable binary object --->
<cfset ivString="3a513d6df56cd0fd">
<cfset ivBase64=toBase64(ivString)>
<cfset ivBinary=binaryDecode(ivBase64, "Base64")>

<!--- Convert the key to a Base64 encoded string --->
<cfset keyString="b341813267566422">
<cfset keyBase64=toBase64(keyString)>

<!--- AES-128-CBC encrypted string (Base64 encoded) --->
<cfset enc = replace(urlDecode(url.e), " ", "+", "all") />

<!--- This is the cipher method to use --->
<cfset method="AES/CBC/PKCS5Padding">

<!--- Decrypt the encrypted string --->
<cfset dec=decrypt(enc, keyBase64, method, "Base64", ivBinary)>

<!--- Show the results --->
<cfoutput>
<p>
<b>Encrypted:</b> #enc# <br />
<b>Decrypted:</b> #dec# <br />
</p>
</cfoutput>


Notes:
  • We had originally tried to use a 256-bit cipher, but it turned out that a Java Cryptography Extension is needed for ColdFusion to support higher than 128-bit encryption. So, we decided to use a 128-bit cipher instead.
  • When you're using a 128-bit cipher, the key needs to be exactly 128 bits (16 characters) long. PHP does not seem to care if the key is too long (I suspect it just truncates it to the required length), but ColdFusion will throw an error.
  • The call to replace() on the ColdFusion end is needed to work around a weird issue. The encrypted string can contain the + character, and the urlencode() function in PHP replaces + with %2B. However, the urlDecode() method in ColdFusion decodes %2B as a space. We need to catch that and make sure the + remains intact.

2 comments:

  1. thanks for the guide this safe mine, and for those who need to random the string, you can try it here www.random.org/strings

    ReplyDelete
  2. Having to deal with this same thing this week...a life saver as the php server is outside the network and our coldfusion server is inside the network...so, had to securely and safely get the data...many, many thanks.

    ReplyDelete