1
votes

I have been seeing a lot of problems and resolutions of Coldfusion Decrypt function. However, I have not come across this one at all.

I can encrypt and decrypt properly. I generated a key and encrypt it with:

<cfset encrytedpwd = encrypt(password, mykey, "AES/CBC/PKCS5Padding", "hex")> 

This is done in program #1. Then in a different program #2 I decrypted it with:

<cfset mypwd = decrypt(encrytedpwd, mykey, AES/CBC/PKCS5Padding", "hex")> 

which is great and works perfectly.

Now the problem: the password for all the records are not always encrypted. Some are encrypted because they passed thru program #1. But some old records are not encrypted. So when the password is retrieved as "encrytedpwd", the decrypt function will crash with the error:

An error occurred while trying to encrypt or decrypt your input string: ''.

I would expect the CF Decrypt function to return maybe "false" or some kind of warning rather than cause the CF to crash.

Another scenario is that if someone temper with the value of the encrypted password before passing to program #2, it will also crash.

Is there any way to check the value of password that it is hex or not before passing to the Decrypt function so that it won't crash ungracefully?

Thanks a lot.

Edited:

I added the try/catch in the code and it does not catch the exception at all.

On top of the CF activate.cfm I include a template call handlerror.cfm which has the following:

<cferror
    template = "exception.cfm"
    type = "exception"
    mailTo = "[email protected]"> 

The exception.cfm is simply a formatted error page:

...
<li><b>Your Location:</b> #error.remoteAddress#
<li><b>Your Browser:</b> #error.browser#
<li><b>Date and Time the Error Occurred:</b> #error.dateTime#
<li><b>Page You Came From:</b> #error.HTTPReferer#
<li><b>Message Content</b>:
<p>#error.diagnostics#</p> 
...

The activate.cfm has

<cfinclude template="handlerror.cfm">
<cfif isdefined("url.chk") and isdefined("url.auth")>
    <cfif len(url.chk) gt 0 and len(url.auth) gt 0>
        <cftry>
        <cfset uid=decrypt(url.chk, mykey, "AES/CBC/PKCS5Padding", "hex")>
            <cfcatch>
                <div class="msgcenter">
                <div class="warning">Sorry, there seems to be an issue with the activation </div><br>
                </div>
                <cfoutput>
                <!--- The diagnostic message from ColdFusion. --->
                <p>#cfcatch.message#</p>
                <p>Caught an exception, type = #CFCATCH.TYPE#</p>
                <p>The contents of the tag stack are:</p>
                <cfdump var="#cfcatch.tagcontext#">
                </cfoutput>
            </cfcatch>
        </cftry>
        <cfif REFindNoCase("[^a-z0-9_]", uid)>
            <div class="warning">Problem with User ID.</div><br>
        <cfelse>
.....

So what is happening now is, if the url.chk is tempered or invalid, it goes to display the exception but for the undefined UID for the REFindNoCase because the Decrypt didn't work in the first place and now it is skipping it because of try/catch.

Without the try/catch the exception would show the error message "An error occurred while trying to encrypt or decrypt your input string: ''." But because of the try/catch it is not doing that now. So I am stuck.

1
At a minimum you can wrap it in a try catchMatt Busche
so that it won't crash ungracefully? Even if the input is hex, there is no guarantee it will decrypt with your key. In short, there is no warning feature of decrypt. Either it works, or throws an exception that you must handle. If you really cannot make the values uniform, then as Matt suggested, wrap it in a try/catch. Though, passwords should be hashed - not encrypted.Leigh
The values can be uniform. But of course encryption is to prevent people from tempering. But if they do, CF shouldn't be throwing exception just because it can't decrypt. Why password should be hashed not encrypted?Jack
BTW, I try the CFTRY and CFCATCH and they don't work. I have generic page with CFERROR that display the exceptions. It went there instead AND bypass the Decrypt exception. It did not display the message I set within the CFCATCH.Jack
CF shouldn't be throwing exception just because it can't decrypt. Yes, it should because that is how the function is designed. It either decrypts successfully or throws an exception. Wanting it to behave differently isn't going to change the behavior of the function ;) Try/catch works fine. (Though consider switching to Application.cfc/onError instead of cferror). If it is not working for you, then you are doing something different on your end. You need to post a repro case.Leigh

1 Answers

1
votes

Wrapping in a try/catch block worked for me in both Railo and CF. I see that you're using a <cferror> page, but if that is indeed interfering, you can run this code once outside the application so that the try/catch will engage or temporarily disable the cferror.

Because of your CFERROR, I would run a script across your table as below, update the non-encrypted users with a flag field. Then you can either

  1. Run a script to automatically encrypt their data and set the prior-mentioned flag to false.
  2. The more responsible thing, since their data wasn't encrypted, would be to require them to change their password, encrypt the new value, and then set the flag to false.

Code:

<cfset PlainPWsList = "">
<cfoutput><cfloop query="CheckPWs">
  encrypted password: #i#<br/>
  <cftry><cfset AttemptDecrypt = Decrypt(password...)>
    <cfcatch type="any"><cfset PlainPWsList = ListAppend(PlainPWsList,userID)><!--- This password wasn't encrypted ---></cfcatch></cftry><br><br>
  </cfloop>
  </cfoutput>

<cfquery>
update users
  set forcepwchange = 1
 where userID in (<cfqueryparam cfsqltype="cf_sql_integer" value="#PlainPWsList#" list="yes">)
</cfquery>

And then, when the user logs in, you can make a decision based on the value of the flag. You could even do that entirely in the query.

select userid,stuff from users
 where username = <cfqueryparam cfsqltype="cf_sql_varchar" value="#form.username#">
   and ((password = <cfqueryparam cfsqltype="cf_sql_varchar" value="#encrypt(form.password)#"> and forcepwchange = 0) or (password = <cfqueryparam cfsqltype="cf_sql_varchar" value="#form.password#"> and forcepwchange = 1))

Now, as Leigh brought up: Why aren't you hashing your passwords?. The only almost-justifiable "use" for not hashing your passwords is password recovery and really, allowing the user a reset-route is much better. Encryption/decryption otherwise only allows staff members or prying eyes to see the passwords. Even if you've designed a system where admin can login to user accounts for customer service purposes, a secondary gateway can be written so that the admin doesn't need to know the user password.

Here's what I'd do.. I'd step up the flag.

0 = Password is hashed and fine. While salting hashes is a great thing, there's no real need to encrypt and then hash. On your change password page that users from either list arrive at, I'd just salt and hash the data. Salt because there are tables of straight hashes of entire dictionaries (and given people's penchant for the simplest passswords possible, this is important to help them avoid).

1 = Password is encrypted and needs hashed.

2 = Password is plain text and needs hashed.

<cfset PlainPWsList = "">
<cfset EncryptedPWsList = "">
<cfoutput><cfloop query="CheckPWs">
  encrypted password: #i#<br/>
  <cftry><cfset AttemptDecrypt = Decrypt(password...)>
    <cfset EncryptedPWsList=ListAppend(EncryptedPWsList,userID)>
    <cfcatch type="any"><cfset PlainPWsList = ListAppend(PlainPWsList,userID)><!--- This password wasn't encrypted ---></cfcatch></cftry><br><br>
  </cfloop>
  </cfoutput>


<cfquery>
update users
  set forcepwchange = 2
 where userID in (<cfqueryparam cfsqltype="cf_sql_integer" value="#PlainPWsList#" list="yes">)
</cfquery>


<cfquery>
update users
  set forcepwchange = 1
 where userID in (<cfqueryparam cfsqltype="cf_sql_integer" value="#EncryptedPWsList#" list="yes">)
</cfquery>

And then your login query

select userid,stuff from users
 where username = <cfqueryparam cfsqltype="cf_sql_varchar" value="#form.username#">
   and ((password = <cfqueryparam cfsqltype="cf_sql_varchar" value="#hash(saltedpassword)#"> and forcepwchange = 0) 
    or (password = <cfqueryparam cfsqltype="cf_sql_varchar" value="#encrypt(form.password)#"> and forcepwchange = 1)
    or (password = <cfqueryparam cfsqltype="cf_sql_varchar" value="#form.password#"> and forcepwchange = 2))