47

Possible Duplicate:
Which password hashing method should I use?

There are a ton of great posts about password security in databases on stack overflow and on other sites and as I am completely new to this I spent quite some hours trying to learn more about it in the last days. However, there are so many different suggestions and best practices that I still got very confused...

Also, there are still many older posts from 2007-2010 etc I as I saw how fast things change I am not sure if this is still common to use it like they suggest. So, I wanted to summarize what I found in the posts and then ask if this method I found was a good practice...

So, this is not a guide, but a summary, I know it is very very basic and if there are mistakes please correct me!

  1. Just to mention: you should never store plain text passwords :)
  2. You "one-way" hash passwords, so nobody can ever see the plain text. When the user enters the password, you hash it the same way and compare it with the hashed pass in the database.
  3. In addition to hash a password, you should salt it. You add the salt to the plain password string and hash the new string. If the plain password is weak, adding the salt makes it longer and stronger.
  4. The salt should furthermore be random (I've read that the term "salt" means it is random anyway, otherwise it was called "key"?). This is to prevent rainbow table attacks, because the attacker would need to create one table for each salt which is much more expensive in terms of time etc. Also if two users have the same password you will not recognize it if you use random salts.
  5. The salt is not a secret! It is being stored in the database next to the hashed password. However, it may not be the best idea to use a timestamp, the user's e-mail address or anything else connected to the user as a random salt. As a reason it is e.g. mentioned that users tend to use the same passwords on multiple sites/services. So, the salt should be a random string, I've read best would be 64-bit?
  6. The next step is to add iteration to the hashing process (1000 or more loops), so the salt is added to the password and they are then hashed over and over, which means only a fraction of a second for the user to wait when logging in, but sums up if you have somewhat 10.000 entries in your DB.
  7. It might be a slight benefit, if you add a site key, stored e.g. as a global var in addition to the salt. However, you should always suppose that attackers have access to your file system as well.
  8. Hashing algorithms: I found for sure it is not secure anymore to use MD5, SHA1 and other weak methods...

So, there are different opinions on wether to use SHA256, SHA512? Should you use hash_hmac? Some say yes: (http://rdist.root.org/2009/10/29/stop-using-unsafe-keyed-hashes-use-hmac/) some say using libraries is the only secure way... and then once or twice in a post I've read not to use libraries as bcrypt or bubble fish??

Is it really necessary to use a library or is e.g. a method like this enough:

function hash_password($password, $nonce) {

  for ($i = 0; $i < 5000; $i++) {
    $hashed_pass = hash_hmac('sha512', $hashed_pass . $nonce . $password, $site_key);
    }
  return $hashed_pass;
}

Many people say don't invent your own algo, so I am a little bit scared just to use anything self-invented.

I can imagine it is hard to predict, but how long can a method used to today be considered as being secure enough?

UPDATE: So, thank you for all your great feedback. There is one main point missing, as I see and many of you said: password security, meaning a strong password is the basic. I think I got the message, thank you again! :)

UPDATE 2: Just for completion, I have found following code on http://www.lateralcode.com/creating-a-random-string-with-php/ that I use to generate a random salt:

function rand_string( $length ) {
    $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!§$%&/().-:;_#+*[]{}="; 

    $size = strlen( $chars );
    for( $i = 0; $i < $length; $i++ ) {
        $str .= $chars[ rand( 0, $size - 1 ) ];
    }

    return $str;
}

$random_salt= rand_string(20);
Chris
  • 481
  • 5
  • 6
  • 2
    One of the better questions I've seen in a while. =) –  Feb 29 '12 at 09:44
  • As zerkms commented and referred to php.net, I also thought about an iteration of 5000 times... you all say it makes no sense? is it the right way the function is built or what did I do wrong with overwriting the variable in every loop? @zeta two: thank you, I did not expect it... –  Feb 29 '12 at 09:54
  • so, a user tends to use the same timestamp to register on multiple sites/services? interesting... –  Feb 29 '12 at 10:04
  • @Col. Shrapnel: you are right, the reason not to use a timestamp was, as far as I remember, the fact that a timestamp is easy to guess, it only consists of numbers etc. whereas a secure, random salt containing big and low characters, special characters etc. might be the better way to go... it is just what I've read... –  Feb 29 '12 at 10:09
  • well from the (5) we are assuming that the salt is not a secret already. Why guess it? ;) –  Feb 29 '12 at 10:19
  • well, what do you think would be a better salt: 123 or %AShlkk...(20 characters)? of course you know the salt if it is stored in the database and as said, you should suppose hackers have access to your file system as well, but in case they don't you can use a site key in addition, or like it was mentioned above if hackers know the username and try to login via the login form? But anyway, if you use a times tamp it is your choice... like again, I just summarized what I've found in quite some posts on stack overflow, so quite some people think it is better not to ;) –  Feb 29 '12 at 10:26
  • Okay, okay, you just read that others wrote. What about to think it over a bit? if hackers know the username and try to login via the login form - so what? what does salt to do here? –  Feb 29 '12 at 11:15
  • Sorry, Col. Shrapnel.. do you want to say your timestamp approach is the best and only one? maybe look here: http://stackoverflow.com/questions/4983915/is-time-a-good-salt and furthermore I guess if you are always trying to find a: "but what in this case??" you can find one... like I said: please use your timestamp if you are happy with it. And I did not only read what others wrote, I also made my thoughts but don't want to discuss the whole point about it again. –  Feb 29 '12 at 11:28
  • I am talking not of timestamps generally, but of *thinking*. But it seems you don't like it. Well, "please use your non-thinking approach if you are happy with it." –  Feb 29 '12 at 11:37
  • I didn't want to offend you, Col. Shrapnel and I think it is not the right place here for such comments... however, I appreciate your think-about-it approach, but did not feel that you considered my arguments, like a random salt is better than only numbers, and to add a site key in addition that is not stored in the file system etc. I am not a hacker, just assumed that if you know the salt is a timestamp like you did suggest and you know the hash algo and e.g. the username you're almost done using the login form, or am I completely wrong with it? maybe I am missing something here? –  Feb 29 '12 at 11:44
  • Sorry, you provided no *arguments.* "random salt is better than only numbers" is not an argument. It's just a statement. And statement can be false or true. I am assuming it false until it is **proved** with some *argument.* Let me show you an example: All possible weaknesses, listed in the question you linked, has nothing to do with timestamp salt. It is not linked to the user (so no attack to other user's account possible) and it is not needed to be predictable as it is already assumed as not a secret one. Can you describe a *certain* attack using such a salt? What did you say about a form? –  Feb 29 '12 at 12:02
  • what I meant with random is that it contains characters, special characters etc as well as I did mention above. Maybe all what I said was wrong, can be... however then explain the 40 up-votes on the linked topic with the statement "No, time() is not a good salt." and a very detailed description below... maybe it is better if you make your points at this topic then because I may not understand your logic and cannot follow your thoughts either completely... –  Feb 29 '12 at 12:33
  • 1
    but as far as I understand the topic is about creating a unique salt that is unique worldwide which is not true if you use a timestamp. And the question is EXACTLY about using timestamp, so I did not get your statement that it was not about timestamps Col. Shrapnel?? –  Feb 29 '12 at 12:36
  • 9
    **Caution**: This question was migrated from Stackoverflow with many answers of dubious quality that already had a lot of votes -- more than the IT Security community can correct for. As a result, you should not trust the answers here, since the IT Security community is not able to mass enough votes to ensure the quality of the answers. **Comment:** This question has [already been answered](http://security.stackexchange.com/q/5605/971) in depth [several times](http://security.stackexchange.com/q/4781/971) on IT Security. Please consult those pages, not this one, for good advice. – D.W. Mar 01 '12 at 03:26

5 Answers5

10

You forgot just one little thing:

Password strength.

Just 2 words which overweight whole your brilliant topic.

There are indeed hundreds of topics on Stackoverflow telling you to use this or that hashing algorithm and random salts.
And only very few explaining that with weak password all your eight-item-list protection, all extra secure hmac-chmak-bumblemak hashes with 2048 bytes-long-salt will be broken in a matter seconds.

As for the poor users who can't remember $#%H4df84a$%#R passwords - just make them use not a password but a passphrase.

Is it a good day today, Winkie? Uses different case letters as well as password of recommended complexity but much easier to remember.

Of course the phrase shouldn't be as common as common passwords like "Joe" or "123".
"Good morning", "Oh My God! They Killed Kenny!" phrases should be avoided.
But by adding some personality it would be okay:
Oh My God! They moved Cthulhu! looks quite enough

Another thing to mention is a secure communication between server and browser
Without it, a plain password can be easily "sniffed" out on the any of the routers involved in the communication.

SSH or some implementation of Digest authorization is no less essential.

  • +1 for the passphrase, that helps people and improves security. –  Feb 29 '12 at 10:42
  • so, the passphrase is being used in addition to the password? I also saw sites, on which you first only enter your username. If this one was correct you see a security image and a security passphrase and need to enter your password... what is the idea behind it? –  Feb 29 '12 at 10:47
  • 2
    I think Col means, that users tend to use weak passwords, because strong passwords are hard to remember. Then it will be better, if they use a whole phrase **as** password. You could encourage this, with a tip on your page. –  Feb 29 '12 at 10:53
5

A general purpose hash function (MD5, SHA1, SHA256) with a good hash might be secure enough for most smaller web sites, given the current computing power. However, Moore's law will ensure that even good passwords will be exposed to brute force attacks in the near future. The issue is that hash functions can be computed much too quickly. The best solution is to use bcrypt or PBKDF2 with a high number of iterations. Bcrypt vs PBKDF2 was discussed at http://security.stackexchange.com.

Some people might think this is overkill for most sites but remember that passwords are reused all the time and that password storage is usually something you will not change until there is an issue.

Recommendations:

  1. Use bcrypt or PBKDF2. DO NOT USE SHA3 candidates. They might contain security holes that have not been discovered yet.
  2. Use a random salt to help protect against rainbow table attacks.
  3. Force users to select a passphrase.

More information

tony
  • 283
  • 1
  • 8
  • what if my salt is not random? wouldn't it protect from rainbow table? –  Feb 29 '12 at 17:35
  • Its not ideal but much better than not using a salt. Do you mean something like a timestamp or a constant salt? – tony Feb 29 '12 at 17:46
  • 1
    Salt ideally should be unique, long, and random, in that order in my opinion. – tony Feb 29 '12 at 17:57
  • @tony so you recommend only using crypt or pbkdf2 is secure enough? The article you linked is very good, however, also hmac function is mentioned to be more secure than a simple hash. Secure enough? If you add iterations it is not yet slow enough? I've seen the wikipedia article about brute force attacks (http://en.wikipedia.org/wiki/Brute_force_attack), which mentions calculation times... what is your opinion on this? And: what is the difference for you between a unique and a random salt? –  Feb 29 '12 at 18:57
  • +1 for bcrypt, although IIRC in most implementations, it sorts out the salt for you so you don't need to generate/store it yourself. http://stackoverflow.com/questions/6832445/how-can-bcrypt-have-built-in-salts has a good description – Rory McCune Feb 29 '12 at 18:57
  • @Chris I don't think bcrypt or pdkdf2 are the only options secure enough. Those two are my two quick recommendations. The two major concerns is that it is slow enough and it has a salt. My particular recommendations have more to do with them being easily available, easy to use, and popular. – tony Feb 29 '12 at 19:38
  • @Rory You are correct. – tony Feb 29 '12 at 19:44
  • @Chris Also, you can use hmac with pdkdf2. You can do the iterations yourself but I wouldn't recommend it. – tony Feb 29 '12 at 20:23
2

General info:

Nice question but all I can say is it totally depends on the usage. If you use it only for a small website, there is a high chance only "script kiddies" will visit you, in that case a simple SHA1 with salt is totally enough today as well. In this case, 5-8-10 years or maybe even more is totally good, script kiddies won't really break into it if they see that they can't break in easily. If your site will be attacked for some reason - monetary site, etc. then you should use the newest available functions. In this case I'd even "risk" to use a non-official but already ported function. Right now there's an SHA3 competition, for websties which'll have high money in it, I'd use one of the 5 competitors of these.

The points I say that are important:

1. use a random salt, perhaps even 20 characters. It makes a lot harder to break passwords, and even if they are breakable, it takes too much time to be worth.
2. use SHA-1, SHA-2 or WHIRLPOOL. With good salt, MD5 is OK as well, but I still suggest these.
3. you can use any great cryptography if users' passwords are trivial. You must make sure they use non-dictionary, long passwords with uppercase, lowercase letters, numbers and symbols else it is easily breakable by brute force. You might consider that they could be forced to change password every year or so.

To the code:

Looping many times could be useful but I don't think 5000 is required. 3 for normal security is good, or 100, maybe 1000 could work, but I think 5000 is just too much. SHA-256 is good, but you could use an algorithm specifically developed for that, see the comments.

axiomer
  • 178
  • 5
  • wow... thank you for this answer! users can book and make reservations, send messages etc. so it should be protected against more than just "script kiddies" ;) and, what is actually the point in using SHA1 if there is a better way available? Is it only because of computing time? –  Feb 29 '12 at 09:42
  • Concerning secure passwords: can you really force your users to use uppercase and lowercase and special characters and a minimum length of 9 or so? I don't know how many sites really do this? Isn't a salt for this too?? –  Feb 29 '12 at 09:47
  • Mostly yes. In the documentation of PHP's hash() function, someone submitted some datas on time. From the commonly used algorithms, MD5 was the fastest (I don't consider MD4 as commonly used), 6890 microseconds, then SHA1 8886 microseconds and SHA256 was 19020 microseconds. (Nearly 3 times as SHA1). Runtime is very important: the next member of MD family, MD6 didn't even get to round 2 of SHA3 competition because of runtime. Sources: http://www.php.net/manual/en/function.hash.php and http://csrc.nist.gov/groups/ST/hash/sha-3/Round1/submissions_rnd1.html – axiomer Feb 29 '12 at 09:48
  • 1
    Chris: not really. Imagine this situation: somehow hacker A breaks into your server, gets to the database and sees your coding. A saves the database to their own server and tries to brute-force password of B. If B's pass is "Benjamin", "78492823", or even "damn16websites", A will break it with simple dictionary attacks. But if it's "60EaD$qőPo", they won't. – axiomer Feb 29 '12 at 09:50
  • 1
    Addition to this: they don't even have to break, just have the info that username of B is "whateveritis" and then they'll try to log in with this with many dictionary-based passwords. And they might be successful. You might do that aftern (3? 5? 10?) tries, it won't let people login, but then B will be angry that they cannot login because a hacker has tried to login and leaves your site and goes to a competitor. – axiomer Feb 29 '12 at 09:54
  • axiomer: hackers trying to log in via the login mask is a very good point, actually! Furthermore I read that common usernames as "admin", "root", etc. are even more in danger... so, how can you best prevent that? And in case you let users try to log in say for 10 times (which should be enough, don't you think so?) do you store this value in the database, right? A cookie or ip alone won't do it :D –  Feb 29 '12 at 09:59
  • Yes, you store it in the database as cookies can be deleted pretty easily and IPs can be changed with proxies or using dinamic IP providers. And for admin, root, I say you shouldn't even let these to be in the username as they might be misleading to another users (but this isn't related to password security but it's because of the scam possibilities). – axiomer Feb 29 '12 at 10:02
  • OK, well the username will not be visible to other users... just as e-mail addresses may change I thought of using usernames to log in... –  Feb 29 '12 at 10:04
  • I think that these common usernames like admin, etc. should not be allowed. Also, take the example of paypal: they use email address to login. If it's good for paypal, it is good for you. Why is it not a problem to use them? They can login with expired email address then they can change so they will have a valid one later. But if you wish to use usernames, that's reasonable as well because of what you mentioned but usernames are much more easy to guess than emails: if someone is called "Alice", there's a chance they'll login as alice even if their email is alice.johnson.1980@example.com – axiomer Feb 29 '12 at 10:10
  • 1
    ok, you are right axiomer, to some point... probably the username should as well have a minimum length. I just thought if you know the email address from somebody or you have a list of emails or common names you could try firstname.lastname@somedomain.com?? –  Feb 29 '12 at 10:14
  • Yeah right. And yes, if you decide for usernames, they really should have a minimum length, for example 5 or 7 characters. – axiomer Feb 29 '12 at 10:19
  • I strongly disagree with the suggestion to use one of the SHA3 candidates. Read up on the competitions for previous hashing/encryption algorithms. You'll be taking a very real risk of using an algorithm that will turn out to be broken. – Dan Is Fiddling By Firelight Feb 29 '12 at 13:50
  • Yep, perhaps you're right. I said it's only good to use if for some reason even SHA2 isn't affordable – axiomer Feb 29 '12 at 14:33
  • Why the TWO downvotes? What was wrong in my answer? – axiomer Feb 29 '12 at 15:51
  • 1
    @axiomer this is bad advice. More iterations is the best way to make brute force attacks diffcult. Recommending SHA3 algorithms is also dangerous. – tony Feb 29 '12 at 17:41
  • "but you can use SHA-2 right now safely and later SHA-3." - LATER. I said LATER. And 5000 iterations is WAY too much. 1-2-3 is perfectly enough. – axiomer Feb 29 '12 at 17:48
  • 7
    downvote from me for general purpose hashing algorithms (SHA family or MD5) for password storage I'd recommend an algorithm specifically designed for the problem (eg, bcrypt, pbkdf2). General purpose hashing algorithms are designed to be "fast", which helps the attacker, password storage hashing algorithms are designed to be "slow" which helps the defender. – Rory McCune Feb 29 '12 at 19:00
  • 5
    -1 for suggesting 2-3 iterations. You want to be slow against brute-force attack. So you should use some algorithm which is slow. For SHA512 I certainly wouldn't use less than 1000 iterations. | It's preferable to use an existing iteration scheme such as PBKDF2, bcrypt or scrypt. – CodesInChaos Mar 01 '12 at 14:19
  • You're right. Editing answer. – axiomer Mar 01 '12 at 14:20
2

The hash_hmac() function would be a good choice, it can even overcome problems of a weaker hash algorithm. The only problem is, that it is too fast. That means with enough cpu-power (cloud), creation of different rainbow tables becomes possible, that's the reason for iterating. If you need this high security, then you can measure the time your iterations need, and then calculate the number of iterations needed for a given time.

Edit:

Iterating can solve this problem, it should be done in a way, that one can increase the number of iterations later, without making existing hashes invalid. This is necessary to adapt to new future hardware, but requires to store the hashing parameters together with the hash.

The bcrypt hash algorithm was designed to meet this demands, i wrote a small example of how to use bcrypt with PHP 5.3, i tried to comment it, so one can understand what's going on.

martinstoeckli
  • 5,189
  • 2
  • 27
  • 32
  • thanks for the info... and yes, this would be interesting if iteration is helpful, as there seem to be different opinions on it... –  Feb 29 '12 at 11:47
-1

Well first of all, you are not inventing an own algorithm. You are using an existing one (sha512).

Secondly, you loop 5000 times, that will not do you any good. Only a couple of times will be sufficient.

Thirdly, if you are storing a password with sha512, and add a good seed, it is really really hard to get the password back. And unless you are with FBI or something, it should be considered safe enough as it will take ages to decrypt 1 password, let alone your entire user database.

Topener
  • 107
  • 3
  • Oh, OK... thanks Topender! So, what would be a better way not to overwrite the same variable then? Sorry, if this is a trivial question... –  Feb 29 '12 at 09:33
  • 2
    "Secondly, you loop 5000 times, but you are overwriting the same variable, that will not do you any good." --- it will. It makes the computation slower –  Feb 29 '12 at 09:35
  • why do you loop in the first place? –  Feb 29 '12 at 09:35
  • well, looping is being considered as strengthening, isn't it this term? –  Feb 29 '12 at 09:36
  • 5
    @Topener: because it slows its computation. The slower the computation - the more difficult to brute force –  Feb 29 '12 at 09:37
  • excuse, misread it a bit, changed my answer to improve it. –  Feb 29 '12 at 09:39
  • SHA-2 (SHA256 includes this), but even SHA-1 is good for most uses with a good salt. If you use good salt and SHA-2 family, you could be sure of safety if your users use good passwords. – axiomer Feb 29 '12 at 09:40
  • @Topener: 5000 is a common rounds amount. Just ctrl+f for "5000" at http://php.net/manual/en/function.crypt.php –  Feb 29 '12 at 09:41
  • @axiomer: thx for the info! however, how do you consider a good password to be like? and I thought adding a salt is also to make a weak password stronger? –  Feb 29 '12 at 09:44
  • @Topener: take a look at [key stretching](http://en.wikipedia.org/wiki/Key_stretching), it explains why hasing the result over and over again might be a good thing. –  Feb 29 '12 at 10:53