The biggest mistake that a developer can make is to store passwords in the clear.
In today’s post, I want to discuss best practices for storing passwords securely. The examples I will provide are directed towards ColdFusion development. However, this is not limited to ColdFusion; these principles apply to any software development project.
The first best practice of secure password storage is:
Never, ever, ever, ever, ever, ever store passwords in the clear.
The biggest mistake that a developer can make is to store passwords in the clear. I have seen countless ColdFusion websites connected to a database that contain some iteration of the following table:
userID | username | password |
---|---|---|
1 | admin | admin |
2 | info | admin |
3 | webmaster | w3bM@$t3r |
If someone with nefarious intention were to obtain access to this table, all of the usernames and passwords would be compromised. Someone could log in and perform actions as any user in your application, causing a massive security loophole.
Why do developers store passwords in the clear? In discussion with developers and administrators, I’ve been told that they feel the need to know what specific user passwords are, or require a means to be able to look up a user’s password if necessary. This brings us to the next best practice tip:
Let go of the idea of being able to access a user’s password.
As a best practice for secure password storage, you need to let go of the notion of being able to look up a user’s password. If you’re concerned about the security of your application (and you should be) then you must build in reasonable security measures to protect your data. Consider these rules from a security standpoint:
- No user should be able to see any user’s password, including their own.
- A user should be able to change their own password.
- An administrator should be able to assign a randomly generated password to a user, but that password should be changed to a user assigned password on first login.
The bottom line is that you no not need to know, or even be able to look up what a specific user’s password is. Let it go.
Once that notion has been purged from your expectations, you may be asking yourself, “Self… how do I store passwords without storing the password?” It’s simple:
Hash your passwords
ColdFusion, like most programming languages has a built in hash()
function that will perform a one-way encoding of a string into a fixed-length string. ColdFusion’s hash function takes four arguments:
- string – the string you want to hash
- algorithm – the algorithm used to hash the data
- encoding – the character set encoding used to create the result of the hash, defaulting to the “defaultCharset” attribute of ColdFusion’s neo.xml file, but is typically “utf-8”.
- iterations – the number of times ColdFusion will iterate over the hash making it more complex, but also more computationally intensive. This value defaults to 1.
Let’s create a new field in that users table above called “passwordHash” making it a char variable type with a length of 40 characters and run the following ColdFusion script:
<cfquery name=”getUsers”> SELECT * FROM Users </cfquery> <cfloop query=”getUsers> <cfquery name=”setPasswordHash”> UPDATE Users SET passwordHash = <cfqueryparam cfsqltype="char" value="#hash(Password,’SHA’)#"> WHERE (userID = <cfqueryparam cfsqltype="integer" value="#userID#"> </cfquery> </cfloop>
Looking at the resulting table, this is what we see:
userID | username | password | passwordHash |
---|---|---|---|
1 | admin | admin | 4ACFE3202A5FF5CF467898FC58AAB1D615029441 |
2 | info | admin | 4ACFE3202A5FF5CF467898FC58AAB1D615029441 |
3 | webmaster | w3bM@$t3r | 9001EC3B7E9293AF92D5F35014C580779AD5EDA2 |
This is better. Now that we have a hash of the password stored, we can start authenticating users based on the password hash and not the password in the clear. Once we do that, we can drop the password column, and clean up our exposed data. We’re well on our way to best practices for secure password storage!
But we’re not quite done yet. There’s a problem here; the password hash for the admin and info user are exactly the same, since they used the same password. The username/password combination may be distinct, but if one part of that equation matches another record in the table exactly, it’s not as safe as we can make it. The next step is to:
Add some salt
In order to create unique password hashes for each user, we are going to perform a technique called “salting the data.” A password salt is an additional hash, randomly generated, that gets appended to the password in order to guarantee uniqueness. The first thing I am going to do is make some changes to the users table. I will:
- Modify the passwordHash column to be 128 characters in length.
- Add another column called passwordSalt, that is also a char field, 128 characters in length.
The reason for updating these fields is to accommodate the change I am making to the hashing to support the much stronger SHA-512 algorithm in my ColdFusion script.
Looking at my script above, I’ll make some modifications and run the following:
<cfquery name=”getUsers”> SELECT * FROM Users </cfquery> <cfloop query=”getUsers> <cfset passwordSalt = hash(generateSecretKey(“AES”),”SHA-512”)> <cfquery name=”setHashedPassword”> UPDATE Users SET passwordHash = <cfqueryparam cfsqltype=”char” value=”#hash(Password & passwordSalt,’SHA-512’)#”>, passwordSalt = <cfqueryparam cfsqltype=”char” value=”#passwordSalt#”> WHERE userID = <cfqueryparam cfsqltype=”integer” value=”#userID#”> </cfquery> </cfloop>
Running this script, and looking at the table, we can now see very strong, secure password storage.
userID | username | password | passwordHash | passwordSalt |
---|---|---|---|---|
1 | admin | admin | 03C3529A170… | 13C55682B9… |
2 | info | admin | 323FC900F49… | 7559EBC821… |
3 | webmaster | w3bM@$t3r | 5F90E940583… | 9E29A211D39… |
Now that we’ve stored the hash of the salted password, and the salt itself, your authentication will require some modification. You need to get the password hash and salt of the user you are trying to authenticate against and perform the comparison in ColdFusion.
<cfquery name=”Authenticate”> SELECT passwordHash, passwordSalt FROM Users WHERE Username = <cfqueryparam cfsqltype=”varchar” value=”#form.username#”> </cfquery> <cfif !Authenticate.recordCount> <!--- The user is not in the database. Redirect them to the login page with an error message. ---> </cfif> <cfif Authenticate.passwordHash eq hash(form.Password & Authenticate.passwordSalt, SHA-512”)> <!--- User is authenticated. Run whatever code is needed to establish a user session. ---> <cfelse> <!--- User is not authenticated. Redirect them to the login page with an error message. ---> </cfif>
Excellent! We’re no longer authenticating against passwords in the clear. We are salting password hashes before we store them in the database. We’re comparing the hash of the salted password to the user entry. The security of our application is looking much better! But there’s one final step…
Remove Insecure Data
The very last step is to drop the column of passwords in the clear from your table. Once this is done, and users are being authenticated against a password hash and password salt, you will be performing best practices for secure password storage in your ColdFusion application!
Happy coding!
You must be logged in to post a comment.