The ARTISTS table in the cfartgallery datasource used for examples is an excellent example of how NOT to store passwords. First, they are stored in clear text and the column is limited to only 8 characters.
</tbody>
ARTISTID | THEPASSWORD | |
---|---|---|
4 | [email protected] | demo |
10 | [email protected] | demo |
15 | [email protected] | admin |
The next step most people take is to hash the password and store the hash in the database (assuming we increase the length of THEPASSWORD to accommodate it). Hashing provides a one way encoding of a string to a fixed length string. Below shows what the table would look like if THEPASSWORD for each user was hashed using MD5.
<cfset variables.hashedPassword = Hash(form.password) />
</tbody>
ARTISTID | THEPASSWORD | |
---|---|---|
4 | [email protected] | FE01CE2A7FBAC8FAFAED7C982A04E229 |
10 | [email protected] | FE01CE2A7FBAC8FAFAED7C982A04E229 |
15 | [email protected] | 21232F297A57A5A743894A0E4A801FC3 |
What you'll notice is that the hashed password for both [email protected] and [email protected] are still the same. This makes it easier for the attacker to compromise multiple accounts because the input password is the same for both accounts. To make the hashes unique for each user, we need to generate a string to be appended to the password, or a salt. The salt needs to be unique and randomly generated for each user. Both the salt and the hashed password need to be stored in the database. Also, change the hashing algorithm to SHA-512, because both MD5 and SHA-1 are vulnerable to collision attacks.
<cfset variables.salt = Hash(GenerateSecretKey("AES"), "SHA-512") /> <!--- could use Rand("SHA1PRNG") instead of GenerateSecertKey() ---><cfset variables.hashedPassword = Hash(form.password & variables.salt, "SHA-512") /> <!--- insert both variables.salt and variables.hashedPassword into table --->
</tbody>
ARTISTID | THEPASSWORD | THESALT | |
---|---|---|---|
4 | [email protected] |
457AEB091B4F87EB5D 98D9972FB707DBFDC2 E687B5C8ADA550C997 1B89F3BB3C6F863668 67C5F1F8A7935D9DC6 B3BFC4801D97CD9D78 C368DA0022E2022527 A5 |
1BF869C286A1D2A4B0 D704C620C365BA60B1 B2457200D5918B1F50 F7E27806013963E824 D7ADB140994A56298B 0F20B6246768612418 91660224AF2AA91D8C CE |
10 | [email protected] |
2F275B973C5D617F56 A8957F266AE38A20F0 B33DC88143D44F9110 480000D39E78297A6B FAA739C937379FBA33 97DF447142FBC647C6 2289ECA51955446563 54 |
0EDCEE1F72ABD928B1 934EDF6ED0FF963532 D023E6E0F2BFD70513 844489D063AE36CF4F 3144117946959075CA 49F8E5EAC475A0BE32 4266626BDC06CFEA28 37 |
15 | [email protected] |
ED0148FBC717B9F121 77BCA92C390A142264 D0729B7657416E557F 9E0BA064893810C474 5735F88DB85902C2AF D09C40841035C10175 0E570685F7E843F718 F2 |
C8FE4F050FFE75F6FA 023FB34105D766BE43 32EEFA8925676C8803 FDB849FFDFA1CEFF4E 1407030065DAA55A25 92829B5932C7C97292 D49FEAEAA282F5CEC9 DD |
As you can see, THEPASSWORD for all the users is unique because they have been salted.
ColdFusion 10 enhanced Hash() by adding an additional parameter to provide the number of iterations the Hash should be run.
<cfset variables.numIterarions = 1000 /><cfquery name="request.getPwdAndSalt" datasource="cfartgallery"> SELECT THEPASSWORD, SALT FROM ARTISTS WHERE EMAIL = <cfqueryparam cfsqltype="cf_sql_varchar" value="#form.user#" maxlength="50" /> </cfquery>
<cfif request.getPwdAndSalt.RecordCount EQ 1> <cfif request.getPwdAndSalt.THEPASSWORD EQ Hash(form.password & request.getPwdAndSalt.SALT, "SHA-512", "utf-8", variables.numIterarions)> <cfset SessionRotate() /> <!--- Password is good ---> <cfelse> <!--- Bad Password ---> </cfif> <cfelse> <!--- Bad User ---> </cfif>
Additional Resources: