ColdFusion Muse

How to handle TLS1.2 for ColdFusion 9 and Older

Wil Genovese January 31, 2018 6:26 AM ColdFusion, Security Comments (6)

The upcoming Authorize.NET switch to using TLS 1.2 only has a lot of people scrambling to get their servers updated. This has been a long planned transition at Authorize.NET and at many/most/all other payment processing companies. The inevitable facts are that TLS 1.0 and TLS 1.1 are outdated and they are going away. CF Webtools we have been preparing for this inevitable day for the past few years.

ColdFusion 9.0.n is not tested to work on Java 1.8 and I have had cases were certain features of ColdFusion 9 did not work with Java 1.8. I have not tried any older versions of ColdFusion on Java 1.8 and I'm not going to. Adobe has not certified any versions of ColdFusion older than version 10 Update 14 (or ColdFusion 11 Update 2 and older). All of that being said, there is a workaround that uses a 3rd party commercial solution to make TLS 1.2 connections from ColdFusion 9. It works well, but I do not recommend that as a long term solution. The preferred long term solution is upgrading the server(s) and ColdFusion version to currently supported versions. This way there will be security updates to help protect against new threats. The commercial third-party CFX tag will require recoding the CFHTTP calls for the new CFX tag. The tag is CFX_HTTP5 and it is available here.

Follow the installation instructions that comes with the download and then you will have to recode your CFHTTP calls similar to the examples below. The code examples are for the older Authorize.NET Advanced Integration Method (AIM) API calls that you are most likely using in your older ColdFusion CFHTTP calls.

<cfset authURL = "https://test.authorize.net/gateway/transact.dll" />
<cfif AuthNetMode eq "live">
<cfset authURL = "https://secure.authorize.net/gateway/transact.dll" />
</cfif>

<!--- CFHTTP Call - Your code might look something like this --->
<cfhttp url="#authURL#" method="post" result="cfhttp">
<cfhttpparam type="FORMFIELD" name="x_Login" value="#AuthLogin#">
<cfhttpparam type="FORMFIELD" name="x_Password" value="#AuthPassword#">
<cfhttpparam type="FORMFIELD" name="x_merchant_email" value="#AuthEmail#">
<cfhttpparam type="FORMFIELD" name="x_delim_data" value="true">
<cfhttpparam type="FORMFIELD" name="x_test_request" value="#x_test_request#">

<!--- we're using AUTH_ONLY so the card isn't charged until the order is processed --->
<cfhttpparam type="FORMFIELD" name="x_type" value="AUTH_ONLY">
<cfhttpparam type="FORMFIELD" name="x_method" value="cc">

<cfhttpparam type="FORMFIELD" name="x_amount" value="#orderTotal#">
<cfhttpparam type="FORMFIELD" name="x_card_num" value="#cardNumber#">
<cfhttpparam type="FORMFIELD" name="x_exp_date" value="#cardExpiration#">
<cfif isDefined("cardSecurityCode") and cardSecurityCode eq "">
<cfhttpparam type="FORMFIELD" name="x_card_code" value="#cardSecurityCode#">
</cfif>

<!--- If you want an email to go to the customer via authorize.net change this to true. Make sure authorize.net is configured properly. --->
<cfhttpparam type="FORMFIELD" name="x_email_customer" value="#x_email_customer#">

<cfhttpparam type="FORMFIELD" name="x_first_name" value="#billingFirstName#">
<cfhttpparam type="FORMFIELD" name="x_last_name" value="#billingLastName#">
<cfhttpparam type="FORMFIELD" name="x_company" value="#billingCompany#">
<cfhttpparam type="FORMFIELD" name="x_address" value="#billingAddress#">
<cfhttpparam type="FORMFIELD" name="x_city" value="#billingCity#">
<cfhttpparam type="FORMFIELD" name="x_state" value="#billingState#">
<cfhttpparam type="FORMFIELD" name="x_zip" value="#billingZip#">
<cfhttpparam type="FORMFIELD" name="x_country" value="#billingCountry#">

<cfhttpparam type="FORMFIELD" name="x_customer_ip" value="#cgi.remote_address#">
<cfhttpparam type="FORMFIELD" name="x_Email" value="#billingEmail#">
<cfhttpparam type="FORMFIELD" name="x_Phone" value="#billingPhone#">

<cfhttpparam type="FORMFIELD" name="x_ship_to_first_name" value="#shippingFirstName#">
<cfhttpparam type="FORMFIELD" name="x_ship_to_last_name" value="#shippingLastName#">
<cfhttpparam type="FORMFIELD" name="x_ship_to_company" value="#shippingCompany#">
<cfhttpparam type="FORMFIELD" name="x_ship_to_address" value="#shippingAddress#">
<cfhttpparam type="FORMFIELD" name="x_ship_to_city" value="#shippingCity#">
<cfhttpparam type="FORMFIELD" name="x_ship_to_state" value="#shippingState#">
<cfhttpparam type="FORMFIELD" name="x_ship_to_zip" value="#shippingZip#">
<cfhttpparam type="FORMFIELD" name="x_ship_to_country" value="#shippingCountry#">
<cfhttpparam type="FORMFIELD" name="x_Description" value="#description#">
<cfhttpparam type="FORMFIELD" name="x_invoice_num" value="#invoicenum#">
</cfhttp>

<cfset response = cfhttp.fileContent>

To refactor your code you will want to do something like this.

<cfset authURL = "https://test.authorize.net/gateway/transact.dll" />
<cfif AuthNetMode eq "live">
<cfset authURL = "https://secure.authorize.net/gateway/transact.dll" />
</cfif>
<!--- CFX_HTTP5 Call - You'll want to refactor your code in this fashion --->

<cfset httpBody = "x_Login=#AuthLogin#&
x_Password=#AuthPassword#&
x_merchant_email=#AuthEmail#&
x_delim_data=true&
x_test_request=#x_test_request#&
x_type=AUTH_ONLY&
x_method=cc&
x_amount=#orderTotal#&
x_card_num=#cardNumber#&
x_exp_date=#cardExpiration#&
x_first_name=#billingFirstName#&
x_last_name=#billingLastName#&
x_company=#billingCompany#&
x_address=#billingAddress#&
x_city=#billingCity#&
x_state=#billingState#&
x_zip=#billingZip#&
x_country=#billingCountry#&
x_customer_ip=#cgi.remote_address#&
x_Email=#billingEmail#&
x_Phone=#billingPhone#&
x_ship_to_first_name=#shippingFirstName#&
x_ship_to_last_name=#shippingLastName#&
x_ship_to_company=#shippingCompany#&
x_ship_to_address=#shippingAddress#&
x_ship_to_city=#shippingCity#&
x_ship_to_state=#shippingState#&
x_ship_to_zip=#shippingZip#&
x_ship_to_country=#shippingCountry#&
x_Description=#description#&
x_invoice_num=#invoicenum#"
>


<!--- If you want an email to go to the customer via authorize.net change this to true. Make sure authorize.net is configured properly. --->
<cfset httpBody = httpBody & "&x_email_customer=#x_email_customer#">

<cfif isDefined("cardSecurityCode") and cardSecurityCode eq "">
<cfset httpBody = httpBody & "&x_card_code=#cardSecurityCode#">
</cfif>

<cfset cfxhttp = {}>
<cfset headers = "Content-Type: application/x-www-form-urlencoded">
<cfx_http5 url="#authURL#" method="post" out="cfxhttp.body" outqhead="cfxhttp.QHEAD" outhead="cfxhttp.RHEAD" ssl="5" body="#httpBody#" header="#headers#">
</cfx_http5>

<cfset response = cfxhttp.body>

The code is a minor change and relatively easy to do. I've tested this method in a production environment and it works fine. I do not recommend this as a long term solution. The preferred long term solution is upgrading the server(s) and ColdFusion version to currently supported versions. This way there will be security updates to help protect against new threats. If you are on ColdFusion 10 or 11 then the best option is to install the ColdFusion patches and upgrade the Java version to 1.8 then you will be good to go. If you need an experience ColdFusion developer to make these changes then please do contact us, we will be happy to assist.

The CFX_HTTP5 tag uses WinHTTP which is a built into Windows PROXY server. Here is where part of the problem exists. Microsoft didn't update WinHTTP on Windows 2008 Standard SP2. They've only updated it for Windows 2008 R2 and up. See this update (https://support.microsoft.com/en-us/help/3140245/update-to-enable-tls-1-1-and-tls-1-2-as-a-default-secure-protocols-in). This leaves us not being able to use CFX_HTTP5 on Windows 2008 Standard and older.

This is one more friendly reminder to make sure your ColdFusion servers are patched! Either patch them yourself, have your hosting provider patch them. If you need help upgrading your VM or patching your server (or anything else) our operations group is standing by 24/7 - give us a call at 402-408-3733, or send a note to operations at cfwebtools.com.

CAVEATS:

  • This fix will not work for Windows 2003 Server, for any version of ColdFusion, as there is no support from Microsoft for TLS 1.1 or 1.2 in this server version.
  • This fix will not work for Windows 2008 Standard Server (not R2), for ColdFusion 9.0.n and older, as there is no support from Microsoft for TLS 1.1 or 1.2 for WinHTTP in this server version.

  • Share:

6 Comments


Leave this field empty

Write a comment

If you subscribe, any new posts to this thread will be sent to your email address.

  • charlie arehart's Gravatar
    Posted By
    charlie arehart | 1/31/18 6:32 PM
    Good stuff, Wil, as always. And also that previous post about the upcoming "brown out" of authorize.net support of TLS 1x.

    As for the above, someone could make a lot of people happy by creating a cf_http custom tag that would map all cfhttp attributes into the corresponding cfx_http5 ones. :-) Then it's just a 1 character change to switch to it, and switch back if they ever got their Java properly updated.

    Of course, that customtag would also need to convert the CFX's return variables into cfhttp's expected ones, so the rest of their code would not need to change. I know it won't be a trivial effort, but it wouldn't take a skilled person much time at all, I'd think.

    Of course, I'm not insinuating that you or anyone there) should do it. I'm just putting this out there for anyone who may have the time to try to knock one out. It would be a great community contribution, such as if offered on github, where then others could contribute/help out as well.

    If I was free now I'd start it, but as it is I have to get on to another client. :-)
  • Mark Kruger's Gravatar
    Posted By
    Mark Kruger | 1/31/18 8:41 PM
    @Charlie,

    That is a _great_ idea - hope someone does that. ;)
  • Alan Johnson's Gravatar
    Posted By
    Alan Johnson | 3/14/18 4:27 PM
    This article just saved me big time! Than you Wil!! Tho I don't know you, you've become like a brother to me
  • Tracy Treadway's Gravatar
    Posted By
    Tracy Treadway | 3/18/18 12:47 PM
    Thank you so much, Wil! You saved me as well.
  • Mike L.'s Gravatar
    Posted By
    Mike L. | 6/10/18 2:06 PM
    Great article. I am using ColdFusion 9. I am trying to modify my code to PayPal TLS 1.2.

    Any help to convert this code to cfx_http4 would be greatly appreciated. Thank you in advance.

    <cffunction name="getAuthToken" access="private" output="false">
    <cfhttp url="#variables.server#/v1/oauth2/token" method="post" username="#variables.username#" password="#variables.password#">
    <cfhttpparam type="header" name="Content-Type" value="application/x-www-form-urlencoded">
    <cfhttpparam type="header" name="Accept-Language" value="en_US">
    <cfhttpparam type="formfield" name="grant_type" value="client_credentials">
    </cfhttp>
    <cfset response = deserializeJSON(cfhttp.FileContent)>
    <cfreturn response.access_token>
    </cffunction>
    <cffunction name="makePaymentRequest" access="private" output="false">
    <cfargument name="data" required="true" type="string">
    <cfset var accessToken = getAuthToken()>
    <cfhttp url="#variables.server#/v1/payments/payment" method="post" timeout="120">
    <cfhttpparam type="header" name="Content-Type" value="application/json">
    <cfhttpparam type="header" name="Authorization" value="Bearer #accessToken#">
    <cfhttpparam type="body" value="#req#">
    </cfhttp>
    <cfreturn cfhttp.FileContent>
    </cffunction>
  • Wil Genovese's Gravatar
    Posted By
    Wil Genovese | 6/11/18 7:57 PM
    I would start with this. However, I am confused by "makePaymentRequest" UDF. There is an argument "data" being passed into the UDF, yet it is never used. However, the value "req" is used, but I don't see where that is ever set.

    <cffunction name="getAuthToken" access="private" output="false">

       <cfset var postBody = "grant_type=client_credentials">
       <cfset cfhttpx = structNew()>
       <cfset postUrl = variables.server & "/v1/oauth2/token">
       <cfset postHeader = "Content-Type: application/x-www-form-urlencoded" & Chr(13) & Chr(10)>
       <cfset postHeader = postHeader & "Accept-Language: en_US" & Chr(13) & Chr(10)>
       
       <cfx_http5 url="#postUrl#" method="post" out="cfhttpx.fileContent" outqhead="cfhttpx.QHEAD"
       outhead="cfhttpx.statusCode" ssl="5" body="#postBody#" header="#postHeader#"></cfx_http5>
          
       <cfset response = deserializeJSON(cfhttpx.FileContent)>
       <cfreturn response.access_token>
    </cffunction>

    <cffunction name="makePaymentRequest" access="private" output="false">
       <cfargument name="data" required="true" type="string">
       <cfset var accessToken = getAuthToken()>
       
       <cfset var postBody = req>
       <cfset cfhttpx = structNew()>
       <cfset postUrl = variables.server & "/v1/payments/payment">
       <cfset postHeader = "Content-Type: application/x-www-form-urlencoded" & Chr(13) & Chr(10)>
       <cfset postHeader = postHeader & "Authorization: Bearer #accessToken#" & Chr(13) & Chr(10)>
       <cfx_http5 url="#postUrl#" method="post" out="cfhttpx.fileContent" outqhead="cfhttpx.QHEAD"
       outhead="cfhttpx.statusCode" ssl="5" body="#postBody#" header="#postHeader#"></cfx_http5>

       <cfreturn cfhttpx.fileContent>
    </cffunction>