Salting and Hashing Code Example - Security Series #4.4

So let's create some code for salting and hashing password. What I have here is a function that will take in a username and a password and return, the username, a hashed password, and the salt. The idea is that after the function creates these three things it can then pass that struct (or object if you swing that way) to the next method (or DAO) for insertion into the DB.

So we'll start with a quick method stub. We are going to create a simple method that takes in the username and password as a string and it will return a struct. Right now we are returning an empty struct, but I bet we can fix that.


<cffunction name="hashPassword" access="public" returntype="struct" output="false" hint="I will add a user to the database and return a boolean to let you knwo if it suceeded">
    <cfargument name="username" type="string" required="true" hint="Pass in username" />
    <cfargument name="password" type="string" required="true" hint="Pass in password" />
    
    <!--- Return var --->
    <cfset var returnVar = StructNew() />
    
    
    <cfreturn returnVar />
    
</cffunction>

Next we get to the reason we are here. Salting and hashing.

We'll create our local variable for storing the hashed password while we work with it

    <!--- Initialize Password Hash variable --->
    <cfset var passwordHash = "" />
    

Now we'll create a salt. I like to use the CreateUUID function. It's long enough and random enough that it makes a great salt.

    <!--- Salt the password --->
    <cfset var salt = CreateUUID() />
    

Finally, we'll concatenate the password that was passed in with the salt and hash it.

    <cfset passwordHash = Hash(arguments.password & salt, 'SHA-512') />
    

But wait! What's this? I did not mention this part in my previous posts.

    <cfloop index="hashCount" from="1" to="1025">
        <cfset passwordHash = Hash(passwordHash & salt, 'SHA-512') />
    </cfloop>
    

Because we want to make our hackers' lives difficult, we are going to iterate over the hash 1000 or more time. If they are going to be hashing common passwords and sending them to the front end login page or remote login service in a brute force attack on our application, We want it to take 1000 times longer than it would if we did not do this. Your users won't notice the difference. This entire method took 843 16 milliseconds to run for me.

Now we insert the values into the return struct and return it to the calling function or page (or if we wanted to we could pass it onto the next function).


    <cfset returnVar.username="#arguments.username#" />
    <cfset returnVar.password ="#passwordHash#" />
    <cfset returnVar.salt = "#salt#" />
    
    <cfreturn returnVar />
    

Here is the full code.


<cffunction name="hashPassword" access="public" returntype="struct" output="false" hint="I will add a user to the database and return a boolean to let you knwo if it suceeded">
    <cfargument name="username" type="string" required="true" hint="Pass in username" />
    <cfargument name="password" type="string" required="true" hint="Pass in password" />
    <!--- At this point the function assumes that you have already validated the username and password as meeting application requirements --->
    
    <!--- Return var --->
    <cfset var returnVar = StructNew() />
    
    <!--- Initialize Password Hash variable --->
    <cfset var passwordHash = "" />
    
    <!--- Salt the password --->
    <cfset var salt = CreateUUID() />        
    
    <cfset passwordHash = Hash(arguments.password & salt, 'SHA-512') />
    
    <cfloop index="hashCount" from="1" to="1025">
        <cfset passwordHash = Hash(passwordHash & salt, 'SHA-512') />
    </cfloop>
    
    <cfset returnVar.username="#arguments.username#" />
    <cfset returnVar.password ="#passwordHash#" />
    <cfset returnVar.salt = "#salt#" />
    
    <cfreturn returnVar />
</cffunction>

In my next post we will look at the code to validate the user at log in.

Comments
Chris's Gravatar Jason,

Just wanted to thank you for doing this series. Security is nothing to take lightly, and this has opened my eyes on some of the ways I have been doing it.

Thanks again,
Chris
# Posted By Chris | 5/29/08 8:38 AM
Jason Dean's Gravatar @Chris. Thanks! I really appreciate the compliment. You are right, security is not to be taken lightly. ColdFusion actually does a good job of securing some pieces of our applications for us, but unfortunately, if gives us the opportunity to be less diligent about the other pieces. One of my goals in this series is to present which pieces are handled for us and which pieces we need to handle ourselves.
# Posted By Jason Dean | 5/29/08 10:00 AM
Pete Capra's Gravatar Jason,

Still loving the series and this is another great post. Just a quick "Devil's Advocate" question:

I feel a little uncomfortable about purposely slowing down my code just to thwart hackers from brute force attack. Would a suitable alternative to this be something like preventing the user (by session redirect) from logging on after X amount of failed attempts?

Keep up the great work mate!

Pete
# Posted By Pete Capra | 5/29/08 3:51 PM
Michael Brennan-White's Gravatar Jason,

I am going to be inserting data into a mySQL database of a field that I intend to encrypt using the encrypt function in CF using the cfx mode with a very safe key and the field is salted with another secure string before the encryption happens.

Now my question, which would be the best encryption method to use: uu, 64bit or binary?
# Posted By Michael Brennan-White | 5/29/08 3:52 PM
Jason Dean's Gravatar @Pete - So I ran my test again and found that my 843 millisecond timer was incorrect. It turns out that also included reinitializing the application I was testing on. In fact, the 1000 time iteration took about 16ms and even a 10,000 time iteration only took 172ms.

So for our processing it really does not add that much time.

As for your question, the idea is not that we are purposely slowing down our application to avoid the brute force attack. We are purposely slowing down the hacker. As I said, the hacker's attempts will take 1000 times longer, but our preventative code will only take 15-16ms. Keep in mind that all we are doing is overwriting a variable over and over. The hacker, who is doing a brute force attack will have to go through the process of retrieving the password, hashing it, sending a login request, have it fail, hash the password again, send it again, have it fail, etc until he has done that the correct number of time with the correct password. So it will REALLY slow him down.

As for the second part of your question, yes, having some sort of mechanism that monitors login attempts and locks out users that have made too many attempts, whether by session or by IP, is a good idea. But session cookies can be deleted, and IPs can be spoofed. There are plenty of automated tools out there to get passed our attempts to slow them down via clever programming.

Something I always try to remember is: "Hackers are smarter than I am"
# Posted By Jason Dean | 5/29/08 4:32 PM
Jason Dean's Gravatar @Michael,

UU, 64Bit and Binary are not Encryption Methods, they are encoding options for representing encrypted data (which is Binary) as a string for storing in a database. Kind of like if you ToBase64() an image. And I am pretty sure that which one you choose is a matter of preference.

The encryption algorithm is what you want to be concerned with when it comes to the level of Encryption. And for that, with the Encrypt() function in ColdFusion, you have the choice of AES, Blowfish, DES and DESEDE.

I have not done a ton of research on this yet, so I cannot make a firm recommendation on which one to use. I will hopefully get to my Encryption posts (and research) later this summer.

I can tell you that the last time I looked into it, Blowfish, AES, and DESEDE(TripleDES) all seemed like the excellent solutions. But DES should be avoided.

Reference: http://kb.adobe.com/selfservice/viewContent.do?ext...
# Posted By Jason Dean | 5/29/08 4:46 PM
Kathy's Gravatar I am new to cffunction and cfcomponent as I have only written simple pages up til now. I am attempting to see the output of the component and cannot get the return variable. Do you have some example code that
illustrates the invoke method? Any assistance you could lend would be much appreciated.

By the way, thanks for the great information!
# Posted By Kathy | 6/12/08 3:39 PM
Jason Dean's Gravatar @Kathy - I have added a download to this post that contains a rough, but working, example application using the principles and techniques that I have demonstrated in these posts. I make no guarantees on the quality of the code, I only lightly tested it, etc...

Go to the DOWNLOAD link at the bottom of this blog post (right above where the comments start).
# Posted By Jason Dean | 6/12/08 8:11 PM
Kathy's Gravatar Jason,

You are the greatest -- all I needed was something to go by for syntax since my old Ben Forta book is for CF 5! Guess it's time for another book!

Thank you again and I am glad I found your page. Now I'm going to take my new toys and go to work!
# Posted By Kathy | 6/13/08 8:44 AM
Tony Milne's Gravatar Jason,

Awesome work on your salting and hashing posts. I've read a hand full of them now and have been super impressed at the quality and deatil you've expressed.

One thought that crossed my mind to slow down brute force attackers without delaying your regular users (even if it is only taking milliseconds) would be to create a incorrectLoginCount and use it to exponentially delay the user's next login attempt. E.g on the first incorrect login attempt would not delay them at all, the next few would receive minor (but unnoticable) delays and as the incorrectLoginCount grew so too would the delay between each successive attempt. Obviously resetting the counter upon Session expire or successful login attempt if the counter were being persisted to the database.

Not sure on the exact logistics of implementing this, but in my head - it seems like a cool idea!
# Posted By Tony Milne | 10/21/08 2:30 AM
Jason Dean's Gravatar @Tony,

Thanks for the comment and kind words. I am always glad to hear that someone is getting value out of my works.

As for your suggestion of altering the login time for each successive login attempt to slow down hackers. While I agree something must be done to slow them down, i think there are less complicated way to do it. Check out the comments thread in this post.

http://www.12robots.com/index.cfm?mode=entry&e...

Don't take my word for it though. Try implementing your idea in a sample app and see where it takes you. Maybe you'll discover something cool, but it's certain, you will learn something.
# Posted By Jason Dean | 10/21/08 6:55 AM
Raymond Camden's Gravatar Jason - any reason why you just didn't do a sleep for 500ms?
# Posted By Raymond Camden | 6/29/10 10:32 AM
Jason Dean's Gravatar Ray,

Thanks for the question. It's been a while since I wrote this, and I had to go back and read it :)

I see that I did not do a good job of explaining the purpose of iterating over the hash. Perhaps at the time I did not understand the purpose as well as I do now.

If all we were trying to do was slow down the hacker's attempts against the login service, then using sleep would work great. The part that I did not explain was if the password table or list becomes compromised.

If the table that contains all of the passwords is stolen and all of the hashed passwords are exposed to a hacker, then simple passwords that are only hashed once will be more vulnerable to a rainbow table attack. Because the password "Password1!" will always hash to the same value.

But by hashing every password 1000+ times we are generating hashes of hashes of hashes etc... And then to brute force against the compromised list the hacker must hash the password's hashes and check every one against the hash in the database. Hence slowing down their effort GREATLY.

I hope that makes sense. When I get back from vacation I will update this post to explain more clearly. Right now I am sitting on the balcony in the lodge we rented looking out at the Pacific ocean :) It's cold out here, but it's beautiful.
# Posted By Jason Dean | 6/29/10 12:05 PM
Raymond Camden's Gravatar Jason - did you by change modify the code? I seem to remember seeing it doing this:

<cfset passwordHash = Hash(password & salt, 'SHA-512') />

That's why I thought it was kind pointless. ;) Now it obviously rehashes the hash.
# Posted By Raymond Camden | 6/29/10 12:23 PM
Jason Dean's Gravatar No, I didn't change anything this morning. I have been driving down California highway 1 all day.
# Posted By Jason Dean | 6/29/10 5:30 PM
Raymond Camden's Gravatar Meh - both Jared and I read it the wrong way. We suck. :)
# Posted By Raymond Camden | 6/29/10 5:32 PM
Chris's Gravatar I implemented this scheme some time ago and have found that after upgrading from CF9 to CF10, the hash algorithm has changed such that hashing a salted password with CF10 does not necessarily match the value hashed with CF9. So, many users have had to reset their password. Not good.
# Posted By Chris | 10/4/12 4:19 PM
Jason Dean's Gravatar Do you have any code to reproduce the problem? What algorithm are you using?

Are you saying that it is as simple as if you run this code on CF9 and CF10 that you'll get different results?

<cfouput>#hash("MyString")#</cfoutput>
# Posted By Jason Dean | 10/4/12 5:50 PM
Chris's Gravatar That appears to be the case, I wonder if it's because of the move from JRun to Tomcat? I create salts using the SQL Server command on the salt field in the database like this

set salt=NEWID()

then hash the password like this

Hash(arguments.password & newsalt, "SHA-512")

Wonder if anyone else can confirm this?
# Posted By Chris | 10/10/12 1:49 PM
Jason Dean's Gravatar I just had three people on Twitter try running this code on CF10.

<cfoutput>#hash("MyString", "SHA-512")#</cfoutput>

They all got the output: FCEED6B629240C26D37AC415AD9D275B891D7A9082422A47FA0A70D65A2338AD307E40B32738581CBBD76619843181A50D54CA28A513214E698F9111AB83E789

I ran it on CF9 and got the same output.

Hashing algorithms do not change. There is a standard. A SHA-512 hash of a string on CF9 should produce the same output on CF10, PHP5, .NET 4, Ruby, Python, etc etc.

If the hashing algorithm on CF10 is somehow different, that is a HUGE deal. So far I have been unable to reproduce what you are describing. Can you offer any actual code that I can run to reproduce this? Could the problem be somewhere else?
# Posted By Jason Dean | 10/10/12 2:05 PM
Joshua Cyr's Gravatar I have not seen the hash altered between versions including cf10. Is it possible that the salt you are using is altered somehow? Maybe tied to a server variable that is changed? CGI perhaps?
# Posted By Joshua Cyr | 10/10/12 2:10 PM
Dave Merrill's Gravatar Just to confirm that hash results haven't changed from cf 9 to 10, I have an app whose stored logins would have completely failed if they had. In fact, I have symlinks to the source of this app in sites running under cf 8, 9, 10, railo and open bd, so all those engines are reading and writing SHA-512 hash-based authentication data in the same place, no problems. The app also depends on a javascript implementation of SHA-512 producing the same results as the server side, and again no problem. Jason is right, there is a standard for how these things work, and that's a good thing.
# Posted By Dave Merrill | 10/14/12 7:28 PM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.1. Contact Blog Owner