This article will discuss securing ColdFusion applications with hashed passwords, salt methods, and how to encrypt and decrypt keys. We will also provide a working example of this approach and provide the complete code used in the example.



Example Application

The following example application provides an example of encoding and decoding and how to implement a login form to authenticate users using hashing. The code for this example has been tested using ColdFusion and Lucee, and is shown below.


What is Hashing?

Hashing is a mathematical algorithm that converts a given string into a randomized, unique fixed-length string. This hashed string is nearly impossible to reverse or decode and is often used to secure the authenticity of passwords, digital signatures, and electronic records. Additionally, because hashing uses a one-way encoding method, passwords are not exposed to rogue server administrators or potential hackers if the server has been compromised.


Potential Hash Concerns

There are several potential hash weaknesses. First, there are many different hashing algorithms, and some are insufficient to guarantee the safety of the information. The National Institute of Standards and Technology (NIST) recommends using modern one-way hashing algorithms such as SHA-256 or SHA-512.

Collision is another concern that may occur when two different passwords create the same hash. Collision concerns may exist when the input string (the user's password, for example) is too short, as there is a greater chance that two short inputs will be the same in the database. This is especially true when using weak hashing algorithms. Similarly, collisions may occur with enterprise systems with hundreds of thousands of passwords.

Hashes also may not safeguard weak passwords. Hackers may apply standard hashing algorithms to a dictionary list of common passwords to see if they match to try to breach the server. However, there are methods to alleviate these potential collisions and weak passwords, which we will discuss below.


Hashing Versus Encryption

As we have explained, hashing is a one-way conversion in which the hashed string cannot be reversed or decoded. Encryption, however, is a two-way method of securing data and allows the scrambled information to be decoded as long as the user has the associated security key. Encryption is used when the information needs to be decoded back to its original value. It is often used to secure communications in transit between one or more parties, such as securing bank transactions or medical records.


What is Salt and Peppering?

In cryptology, salt is a technique for adding random characters to the input password to make it more complex before hashing. This associated unique salt key is also kept in the database and added to the password entered by the user to recreate the hash that will be compared to the hash stored in the database.

Peppering, on the other hand, removes the associated secret key from the database. This key is often the same for all users and is generally hard-coded into the application. Both techniques add complexity to the password inputs, reducing the chances of collision and hardening the user's password. 

When either method is used (or both, for that matter), the security of the resultant hash is significantly increased. Theoretically, using brute force attacks with today's hardware, breaking the salted password could take hundreds of thousands of years if you use a strong hashing algorithm. 


Techniques to Encrypt and Decrypt Using Lucee and ColdFusion

ColdFusion and Lucee have functions for encryption and decryption. This article will focus on using the generateSecretKey and the encrypt and decrypt functions. 


The generateSecretKey ColdFusion/Lucee Function

I use the generateSecretKey function to generate the secret key (or the salt when hashing) that we will use to encode and decode a string. We must save the original secret key when decoding or hashing!

This function has several algorithms available; however, I use the Advanced Encryption Standard (AES), which the National Institute of Standards and Technology (NIST) recommends. The other optional argument is key size, the number of bits used in the key. The default is 128. However, I increased the value to 256, which improves security. Consult the ColdFusion/Lucee documentation for more information.


<!--- Note: each user should have their own secret key and this key must be retained to decode the value --->
<cfset salt = generateSecretKey("AES", 256)>

Using the Encrypt and Decrypt Functions 

The ColdFusion/Lucee Encrypt function requires two arguments and the optional algorithm: the key (the password in this case), the secret key, and the algorithm. The last two arguments, encoding and IV salt, are rarely used, and we won't discuss them here.

Like the generateSecretKey, I use AES as the algorithm. The CFMX_COMPAT algorithm used in previous versions of ColdFusion should be avoided as it is no longer considered secure

The decrypt function is nearly identical to the encrypt function; instead of the password, it takes the encrypted string created by the encode function. The same algorithm must be used on both functions for the decode function to work. Both ColdFusion and Lucee functions are identical if you do not use the IV salt argument. See cfdocs if you want to apply an extra IV salt. 


<!--- Using encode decode (avoid this for passwords). Here, we need the key, salt, and algorithm --->
<cfset encryptedKey = encrypt(Form.password, salt, "AES") />
<!--- Decode the key --->
<cfset decodedKey = decrypt( encryptedKey, salt, "AES" ) />

Hashing Functions in ColdFusion/Lucee

The hash function requires the key or string to be hashed. In this example, we are hashing the password. The subsequent three optional arguments are the one-way hash algorithm, encoding, and the number of iterations.

I use the SHA-256 or SHA-512 one-way hash algorithms, which NIST recommends. These algorithms have 64 bits of information and are among the most widely used hash algorithms. If the argument is omitted, SHA-256 is now the default choice for ColdFusion. However, according to the Lucee docs, the default choice for Lucee is MD5, which is now considered inferior and cryptographically broken. Including this argument using SHA-256 or SHA-512 as the algorithm is advisable to ensure you use a viable standard.

If you're using the number of iterations argument, be aware that ColdFusion and Lucee handle it differently

Also, note that we include the secret key (or salt) and the password. Keep the salt and resultant hash values in the database to compare hashes. It does not matter if you use the secret key value (i.e., salt) in front or behind the password; use the same order when later comparing hash values.


<cfset hashedDbPassword = hash(  salt & dbPassword, "SHA-512" )>

Authenticating Users

To authenticate users, the password should be used, along with the salt key, to create a hash (see example above).

After the hash has been made, the hash value and the salt key generated by the createSecretKey function must be saved into the database. We do not want to store the actual password!

During login, the application retrieves the hash value, the salt key from the database, and the user's password entered into the login form to create a new hash string. The user is authenticated if the new hash is the same as the hash value stored in the database.

I have used this general approach to authenticate uses for over a decade, and it has not failed.


<cfif (hash( salt & form.password, "SHA-512" ) is hashedDbPassword)> 
	<b>Pass</b><br/>
<cfelse>
	<b>Fail</b><br/>
</cfif>

Complete Code Used in this Example


<!doctype html>
<title>Password Encryption/Decryption and Hash Demo</title>
<meta charset="utf-8"/>

<cfif structKeyExists(Form, "password")>
	
	<!--- The correct password stored in the system/database --->
	<cfset dbPassword = "galaxieblogrocks!">	

	<!--- Note: each user should have their own secret key and this key should be retained --->
	<cfset salt = generateSecretKey("AES", 256)>

	<h2>Encrypt/Decrypt Logic</h2>

	<!--- Using encode decode (avoid this for passwords). Here, we need the key, salt, algorithm and encoding --->
	<cfset encryptedKey = encrypt(Form.password, salt, "AES", "UU") />
	<!--- Decode the key --->
	<cfset decodedKey = decrypt( encryptedKey, salt, "AES", "UU" ) />
	<!--- Output the values --->
	<cfoutput>
		Secret Key: #salt#<br/>
		Encrypted Password: #encryptedKey#<br/>
		Decoded Password: #decodedKey#<br/>
	</cfoutput>	

	<h2>Hashed Password Logic</h2>
		
	<cfoutput>dbSalt (secret key saved to database): #salt#</cfoutput>
	<br/>Note that you must use the same salt key for both the password in the database and the password typed into the form<br/><br/>
	<!--- Note: you may reverse this and use the salt first and password second. It does not matter which approach you use as long as the validation logic is identical when testing for the correct password --->
	<!--- Save the hashed password and the secret key (i.e. salt) into the database --->
	<cfset hashedDbPasswordSansSalt = hash( dbPassword, "SHA-512" )>
	<cfset hashedDbPassword = hash(  salt & dbPassword, "SHA-512" )>
		
	<!--- Hash the password used in the form --->
	<cfset hashedFormPasswordSansSalt = hash( Form.Password, "SHA-512" )>
	<cfset hashedFormPassword = hash( salt & Form.password, "SHA-512" )>
		
	<cfoutput>
		<h3>Hash Without Salt</h3>
		hashedDbPassword (password in database): #hashedDbPasswordSansSalt#<br/>
		hashedFormPassword (from the form): #hashedFormPasswordSansSalt#<br/>
		
		<h3>Hash With Salt</h3>
		hashedDbPassword (password in database): #hashedDbPassword#<br/>
		hashedFormPassword (from the form): #hashedFormPassword#<br/>	
		<br/>Note: hashes with and without salt are always the same length. In this case, we are using SHA-512, which has 64 bits.<br/>
	</cfoutput>

	<!--- Output the result to the page --->
	<h2>Login Result</h2>
	<cfif (hash( salt & form.password, "SHA-512" ) is hashedDbPassword)> 
		<b>Pass</b><br/>
	<cfelse>
		<b>Fail</b><br/>
	</cfif>

<cfelse><!---<cfif structKeyExists(Form, "password")>--->

	<!--- Interface to authenticate the password --->
	<table align="center" width="100%" cellpadding="2" cellspacing="0">
	 <form id="passwordForm" name="passwordForm" action="index.cfm" method="post" enctype="multipart/form-data">
	  <tr>
		<td align="right" style="width: 20%"> 
			Password 
		</td>
		<td>
			<!-- Password input -->
			<input type="text" id="password" name="password" value="galaxieblogrocks!">
			(galaxieblogrocks! is the password that created the theoretical hash in the database)
		</td>
		<td align="right" style="width: 20%"> 
		</td>
	  </tr>
	  <tr>
		<td></td>
		<td><hr noshade></td>
		<td></td>
	  </tr>
	  <tr>
		<td></td>
		<td>
		  <button id="submit" name="submit" type="submit">Submit</button> 
		</td>
		<td></td>
	  </tr>
	  <tr>
		<td></td>
		<td><hr noshade></td>
		<td></td>
	  </tr>
	</form>
	</table>
		
</cfif><!---<cfif structKeyExists(Form, "password")>--->
	  
</body>

Further Reading