The SitePoint Forums have moved.

You can now find them here.
This forum is now closed to new posts, but you can browse existing content.
You can find out more information about the move and how to open a new account (if necessary) here.
If you get stuck you can get support by emailing forums@sitepoint.com

If this is your first visit, be sure to
check out the FAQ by clicking the
link above. You may have to register
before you can post: click the register link above to proceed. To start viewing messages,
select the forum that you want to visit from the selection below.

This means that the attacker has to regenerate those 300 billion billion possible results to get your salted passwords. And if you use unique salts for each user, that means 300 billion billion per user account!!

If the attacker knows the salt and the seasoning process then a weak initial password is still a weak initial password md5($salt.'password'); is no harder to brute force than md5('password'); & it won't take 300 billion billion anythings to crack

If the attacker knows the salt and the seasoning process then a weak initial password is still a weak initial password md5($salt.'password'); is no harder to brute force than md5('password'); & it won't take 300 billion billion anythings to crack

You are indeed correct. That's why salting is only one part of the equation - salting alone cannot secure your apps. You must also create and enforce password policies (e.g. "must contain upper- and lower-case letters and numbers"). All salting does is render useless rainbow tables, and per-user salts mean that any brute-force attack must be done on a per-user basis instead of all at once.

Originally Posted by G.Schuster

Just to throw one thought into your discussion - what about generating the salt based on username and password?
This would be relatively unique to each user, reconstructable on login and not stored in any way.

Also correct. However, the idea behind the salt is that you are adding data into the equation, not merely repeating it. That said, as I think about it I can't see any reason why this would be any weaker at least than traditional non-secret salts, but I'm not a cryptanalyst, merely a hobbyist. Would be interesting to see someone cryptanalyze this method. Until someone does, though, I'll be sticking with the proven salts.

The difference is that the salt itself is secret to anybody except the user if he knows that the password is the salt.
I think that's much more secure than any salt, even unique ones, stored somewhere.
To calculate the hash (e.g. for a dicitonary attack) you need the salt - to get the salt you need the password - a useful vicious circle.

Actually, if you're doing a dictionary attack, then for each attempt you already have the salt - you just have to repeat your word. This method would, however, be effective in thwarting rainbow tables, just don't fool yourself into thinking that someone brute-forcing the password isn't going to have the salt.

You add a salt to make the hash difficult to brute force, and that does not only include rainbow tables.

As other has mentioned storing the salt together with the hash will make it much easier for the attacker to brute force it, compared to if he only had the hash.

It is also wrong to assume that if the attacker gain access to the user table he will have access to the rest of the database as well. A properly constructed script will use database users with specific access, just to make sure that if one db user is breached, they will only have limit access. The database user who operate the login page, should only have read access to the tables needed etc.

If the hacker "breach" the server, then you have already lost. And there would be no need for him to break your hashes, all he need to do is listen as your users log in. Which leaves server security your top priority.

In my opinion the salt should help prevent any case of offsite brute force attemts (Of course you need to have onsite protection against brute force attempts as well). Either by the use of a global salt, or better by a algorythm creating the salt out of the unique information from each user as well as shifting the location of the salt/password based on the same algorythm.

Also to make the hash almost impossible to break, use chr to include uncommon characters into the hash. Most "brute force" software can not handle uncommon characters. http://no2.php.net/chr

Note: Using a "unique" salt which is stored in the database per user will give you a worse security than using a global stored salt. Due to it will be difficult for the hacker to get access to the global salt without taking over the server.
And even if the hacker get ahold of the salt, it would give exact the same protection as the "unique" salt.

Edit:

In regards to what hash function you use, there is not really a major difference as long as your salt process is good.
I would personally not suggest using anything over sha1 due to the time the function require to run. Even a md5 hash is still secure as long as you use a good salt and a proper login functionality.

People seem to believe that md5 is weak due to the collision possibilities, while in reality that is not true. What will it help me to find a collision? I will not be able to use it at all! The collision value will with the salt added create a different hash, i.e. I got nowhere. And consider that I need to figure out what the salt is I will need to try every possibility of the "collisions" as well (i.e. keep trying to login while removing characters) it will be a extreamly time consuming process. Please note that if the attacker knows the salt then a collision will allow him to login (another reason why you should not store the salt easily located in the database).

In regards of hashing the value several times, I would not recommend it as it does not add any added value. You just increase the processing power your script require to run. A proper brute force software will already rehash the results several times to check if the "script" does it.

Last edited by TheRedDevil; Jun 1, 2007 at 03:17.
Reason: Adding some information

chr() will generate the character based on the ascii value given to it.

An example for the salt can be:

PHP Code:

$salt = 'A490knH*_We'.chr(27).'!/%sa330+",s'.chr(13).'9636uTy..#r^6';

This would if my memory servers me correct create a string including both "escape" and "line break" values. This makes the hash impossible to break for the brute force software which does not include those two characters into the character library. You can also add characters from the extended ascii table, for example the upside down question mark, the cross etc.

TheRedDevil: While in principle you are correct, every cryptanalysis I've seen of salted hashes assumes that the salt is known, and every cryptanalysis I've seen of salted hashes, while they vary in the details, gives incredible boosts to the complexity of brute-force attacks.

I've also seen a cryptanalysis paper that shows that attacks via a method known as "known plaintext" can recover a secret salt based on the input (e.g. the suggestions posted here to use username and/or password as salt) relatively easy. (This paper did not discuss secret salts based on other input. Also, I cannot remember the reference for this paper.) In addition, several papers have discussed the inherent weakness in a single global salt, which once known (and believe me, a determined attacker will eventually determine your secret salt) allows brute force attacks on every password (assuming the attacker has the list of salted passwords).

As to your recommendation to not use a secure hashing algorithm (both MD5 and SHA-1 are broken; MD5 can find in a collision in as little as 2^29 (roughtly 53 million), not the 300 billion billion I quoted earlier), I ran some timings on 5 different hashing algorithms in PHP. I used the hash function for these hashes (hash is often faster than the dedicated md5 and sha1 functions). Here are my results (times are in milliseconds):

So I'm wondering where your claim of these being so much slower comes from? What exactly, if anything, is this based on? Sure, SHA-512 is slower than SHA-256 by 0.007 milliseconds on this run (over 20 runs demonstrated similar results), but both are still faster than MD5! (This was true on every one of more than 20 trials each using a different string (same string for all functions in each trial) - this isn't merely a fluke.) In a web-based application, even under the most demanding of traffic situations, even the difference between the amazingly slow MD5 and the blazing SHA-256 (0.0059 milliseconds) will completely disappear under the overhead of the TCP/IP protocol anyway.

The short is that I see no reason why one should use an algorithm that has been broken when there are more secure and faster algorithms available.

I've also seen a cryptanalysis paper that shows that attacks via a method known as "known plaintext" can recover a secret salt based on the input (e.g. the suggestions posted here to use username and/or password as salt) relatively easy.

If you use known unique values for the salt, then you need a proper algorithm which sort, modify and order the data according to the values it receive. Which means that unless you know exactly how the function works you wont be able to find the salts before you have reengineered all possible cases the function can return.

In addition, several papers have discussed the inherent weakness in a single global salt, which once known (and believe me, a determined attacker will eventually determine your secret salt) allows brute force attacks on every password (assuming the attacker has the list of salted passwords).

Yes, a global salt can be determined if the "brute force" software's character range contain the characters you use in the salt.

Though I am supriced why you believe this is a weakness. You post earlier argued for storing the salt with the hash in the database. This would mean that the "attacker" would get access to both. Hence there would be no need to determine the salt. Only the salt placement need to be figured out.

As to your recommendation to not use a secure hashing algorithm (both MD5 and SHA-1 are broken; MD5 can find in a collision in as little as 2^29 (roughtly 53 million), not the 300 billion billion I quoted earlier)

As I mentioned earlier md5 and sha1 are not really broken, finding a collision will not help unless you know the salt before you start the brute force attempt! (Which is why a salt should not be easily accessed information)

So I'm wondering where your claim of these being so much slower comes from?

The hash functionality is much faster, but it require the PECL module. And unless you are certain that the servers the script will run on contain the module, you should not use it. Using the the dedicated functions, md5 would be faster.

If someone has the knowledge and understand to break into your server I'm pretty sure they aren't going to be script kiddies limited by "Oh no JackTheRipper doesn't support \r as a character!" -- Use chr() characters is pointless; especially in the salt. As has been said, the complexity of the salt is rather irrelevant and the importance is simply the presence of a salt.

Besides, if someone has access to your password hashes, they most likely have access to your salt(s) as well, so there wouldn't be any brute forcing of a salt necessary in the first place.

If you use known unique values for the salt, then you need a proper algorithm which sort, modify and order the data according to the values it receive. Which means that unless you know exactly how the function works you wont be able to find the salts before you have reengineered all possible cases the function can return.

Um... huh? I'm completely lost here. Why do need a "proper algorithm"? What's this sorting, modifying, and ordering that you're doing? Can you please elaborate on this?

Originally Posted by TheRedDevil

Though I am supriced why you believe this is a weakness. You post earlier argued for storing the salt with the hash in the database. This would mean that the "attacker" would get access to both. Hence there would be no need to determine the salt. Only the salt placement need to be figured out.

I believe it's a weakness because using a single global salt allows brute-force attempts against the entire list of passwords instead of having to attack each one independently. For example, if the attacker is executing a dictionary attack, then for the word "aardvark" he need only concatenate the salt, generate one hash value (or two if he doesn't know if you prepend or append the salt), and test against every hashed password, thus finding in one swoop everyone who uses "aardvark" as their password.

Take the same situation now and assume you're using per-user salts. Let's further assume that you have 10,000 users. Now, to find everyone who uses the password "aardvark", your attacker has to compute 10,000 different hashes (or 20,000), thus increasing the complexity of his attack exponentially (essentially, the complexity goes from X with the global salt to X^10,000 with 10,000 per-user salts).

Originally Posted by TheRedDevil

As I mentioned earlier md5 and sha1 are not really broken, finding a collision will not help unless you know the salt before you start the brute force attempt! (Which is why a salt should not be easily accessed information)

I use "broken" in the cryptographic sense - any algorithm for which a method exists that reduces the complexity to below that of the entire key space is "broken". However, you should know that salts don't magically absolve you of the problem of collisions. If user A uses password "xylophone", which has a collision with "aardvark", then yes, adding a salt does prevent "aardvark" from granting access. However, if the salted password has a collision with the salted word "ballyhoo", then "ballyhoo" will work to grant access to the user's account. Salts prevent the use of rainbow tables, but do not magically prevent all collisions in the algorithm.

Originally Posted by TheRedDevil

The hash functionality is much faster, but it require the PECL module. And unless you are certain that the servers the script will run on contain the module, you should not use it. Using the the dedicated functions, md5 would be faster.

Please read the PHP manual:

Originally Posted by PHP Manual

The Hash extension requires no external libraries and is enabled by default as of PHP 5.1.2. It may be explicitly disabled by using the --disable-hash switch to configure. Earlier versions of PHP may incorporate the Hash extension by installing the » PECL module.

The PECL module is not required for the most current PHP, which is what I am coding for. Additionally, my recommendations are for best practices - if you simply don't have access to hash, then use sha1 (it's faster than md5 and, even though it too is broken, is more secure than md5).

The first thing was, why not use a salt from the database, along with one stored in a folder that's not public? It's pretty much a given that if someone can get your files, then they most likely will see what order you put your hashing and salting in, but if all they can get to is the database, this might help a bit.

The other thing I was thinking that was mentioned, how much of a difference would it be to use characters in the salt that aren't in ASCII? For this, would you need to go beyond the used charset's 255's character and start from there?

I'll again reiterate that I've not seen any evidence to suggest that a secret salt helps improve the integrity of a hashed password list. However, I will relent in this case because you are still using a per-user salt along with a secret global salt. Just be sure that you don't accidentally delete that file....

Using non-ASCII characters in your salt will not improve the complexity by an appreciable amount. Ignoring the difficulties with storing non-ASCII data in a database (which, admittedly, are minor and easily overcome, once you know what you're doing), the cryptanalyses I've read have in some cases explicitly stated that non-ASCII salts don't grant you an appreciable improvement. Cryptographically speaking. Of course, brute-force programs generally don't try non-ASCII characters, so you may very well gain a benefit there, however only in situations where your non-ASCII characters come from the secret salt and not the known salt.

If someone has the knowledge and understand to break into your server I'm pretty sure they aren't going to be script kiddies limited by "Oh no JackTheRipper doesn't support \r as a character!" -- Use chr() characters is pointless; especially in the salt. As has been said, the complexity of the salt is rather irrelevant and the importance is simply the presence of a salt.

Besides, if someone has access to your password hashes, they most likely have access to your salt(s) as well, so there wouldn't be any brute forcing of a salt necessary in the first place.

If someone manage to properly break into your server, then they would not need to brute force any hashes. Instead they would just modify the code slightly and receive the correct passwords as the users log in.

However keep in mind, that 98% of the "hackings" that you hear about has actually been done by script kiddies using a automated script targeting holes in specific script's versions. And even if they in many cases can deface sites, it does not mean they have full access to the server.

It is also possible that the database server can be breached, but not the webserver. If that happens the person would get access to the hash, but possibly not the salt (depends on what salt solution you use).

Finally, using a wider range of characters is not only to make "kiddie hacker" tools useless, but also to make the brute force attemt to go slower. You would require more processing power and time to go through all the characters, than you would if you only go through a-z and 0-9.
Having a salt on a few letters only containing a-z will not really improve the security at all. The lenght and complexity of the salt does matter when considering the processing power/time required to brute force it.

Originally Posted by Chousho

This thread has brought up some interesting ideas.

The first thing was, why not use a salt from the database, along with one stored in a folder that's not public? It's pretty much a given that if someone can get your files, then they most likely will see what order you put your hashing and salting in, but if all they can get to is the database, this might help a bit.

The other thing I was thinking that was mentioned, how much of a difference would it be to use characters in the salt that aren't in ASCII? For this, would you need to go beyond the used charset's 255's character and start from there?

Having two salts where one is hidden and the other in the database can improve the security. To improve it even more you could even not include the entire database salt in the hash, which would throw off most attackers as they would try to use the entire salt as long as they dont know your salt processing order.

For using multibyte characters, keep in mind that php does not handle it too well yet (Cant wait until php 6 comes). As long as you dont use the specific multibyte functions php will read it as ascii, so it dont add that much additional protection other than a few multibyte characters, would lead to php reading them as several ascii characters.

kromey:

Um... huh? I'm completely lost here. Why do need a "proper algorithm"? What's this sorting, modifying, and ordering that you're doing? Can you please elaborate on this?

Sure, Ill try to explain a simple version of the class Im currently using for this.

In general it is a simple algorithm which decide what the salt should be.

There are several administrator options, which has to be set when the script is installed. The reason these are here, is so that even if someone own the script, there is no easy way for them to know how the other sites create the salt.

The settings can be, what side to append the salt, perhaps where in the password to append the salt. Should the entire salt be used, or should it be chopped/increased. Should there be any character walks, if how long. etc

Then the actual algorithm takes information which will always stay the same from the information you have stored from the user. Or if desired you can use any information the user has added, though then you need to recreate the password hash each time they change their information (The last one would in the end be more secure as you have more data to process).

Based on a set of calculations according to the information the user has, the class will decide which of the processes that the salt should be created on. Now it create the salt, please note that you should not use all the information you took from the user, only use parts and rearrange that data.

Doing this together with a minor secret salt, will create a salt which is hard to break. Though, if the "hacker" has access to the script in question, and the admin settings then it will be an easy task. Which again brings our focus on that the most importent thing is actually the server security.

I believe it's a weakness because using a single global salt allows brute-force attempts against the entire list of passwords instead of having to attack each one independently.

Ah, yes you are correct here. This is why I use the functionality I explained above. Though in the end a global salt should be complex enough so that it should not be able to be easily found out by brute force. And with collisions its actually even harder to make sure you find the correct salt.
Good point though

I use "broken" in the cryptographic sense - any algorithm for which a method exists that reduces the complexity to below that of the entire key space is "broken". However, you should know that salts don't magically absolve you of the problem of collisions. If user A uses password "xylophone", which has a collision with "aardvark", then yes, adding a salt does prevent "aardvark" from granting access. However, if the salted password has a collision with the salted word "ballyhoo", then "ballyhoo" will work to grant access to the user's account. Salts prevent the use of rainbow tables, but do not magically prevent all collisions in the algorithm.

Yes, but the more complex a salt you use, the less is the chance of finding a valid working collision.

Please read the PHP manual:

The PECL module is not required for the most current PHP, which is what I am coding for. Additionally, my recommendations are for best practices - if you simply don't have access to hash, then use sha1 (it's faster than md5 and, even though it too is broken, is more secure than md5).

Where the "most current PHP version" is the clue, it depends on what you create a script for. In many cases your still forced to create scripts with php 4 backward compability.

sha1 is not faster than md5 through the dedicated functions, at least not when Ive done some benchmarks on my dev server. Though the result might depend on the php versions incase some changes has been made.

But yea, use sha1 if you can due to its more secure. Though even if someone use md5 its not as "unsecure" as the masses wants you to believe. Unless you have a popular site the chance for someone sitting brute forcing all of your hashed passwords are very slim.

In general very few sites require very good protection, and in the cases you do need that you will create a login either based on several passwords, one password and one time codes you send to the user, or a password together with a timebased code creator.

You can never get anything 100% secure, the point is only to make it as difficult as possible for anyone trying to breach your system!

Yes, but the more complex a salt you use, the less is the chance of finding a valid working collision.

Not at all true. To a hashing algorithm, any hashing algorithm, the input is nothing more than a byte stream. Whether those bytes can be represented as printable ASCII is completely irrelevant. Once you understand that, then it becomes painfully obvious that a collision is a collision, and with the myriad attacks possible against MD5 and (albeit to a less extent) SHA-1, a collision is a collision and is completely independent of the complexity of the input data - if the password consists of a string of 348 non-printable ASCII characters, and the salt consists of another 100 non-printable ASCII characters, and the printable word "aardvark" produces an identical salted hash, then all that work you spent coming up with your awesome password was for naught as the Bad Guy(R) gains access to your account with "aardvark".

That is the problem with collisions. Read up on some of the cryptanalyses of hashing algorithms, and you'll find that (surprisingly, actually) the hash is completely independent of the input data in terms of the possibility of finding a collision.

Originally Posted by TheRedDevil

sha1 is not faster than md5 through the dedicated functions, at least not when Ive done some benchmarks on my dev server. Though the result might depend on the php versions incase some changes has been made.

Interesting... I just ran the benchmark again on my own dev and production boxes and found SHA-1 to be twice as fast as MD5 using the dedicated functions. Perhaps this is dependent upon PHP version, though, but I seem to recall finding similar results back when I used PHP4. Alas, I do not have a PHP4 installation to test on at the moment.

Originally Posted by TheRedDevil

But yea, use sha1 if you can due to its more secure. Though even if someone use md5 its not as "unsecure" as the masses wants you to believe. Unless you have a popular site the chance for someone sitting brute forcing all of your hashed passwords are very slim.

I'm sorry, but I have to take exception to this comment. Cryptanalysts are most certainly not "the masses" - they are brilliant mathematicians who each are far more brilliant than you and I put together. Thus, when they tell me to avoid MD5 in all cryptographic applications (and like it or not, securing passwords as we are discussing is a cryptographic application), I listen to them, and not to some ad hominem forum post. Especially from someone who has clearly not read up on cryptography and cryptanalysis.

Originally Posted by TheRedDevil

You can never get anything 100% secure, the point is only to make it as difficult as possible for anyone trying to breach your system!

This seems to be entirely contradictory to your "MD5 is acceptable" rhetoric. MD5 is weaker than SHA-1 every way you look it, and even if SHA-1 is slower than MD5 the few extra milliseconds are definitely worth it for a more secure system, especially in a web-based application where those milliseconds will disappear entirely under the weight of latency and web server overhead.

You're storing a 32byte string (md5). Multiple input strings can md5 to the same result. Now, even though there are a stupid number of possible md5 strings...a brute force has as much chance as ever, regardless of the password.

When i see sql statements like md5(concat(md5('password'),md5(salt))) i cringe. Thats not really -that- much more secure than md5(concat('password'),salt).

Even then, is it worth it to store the salt in the db and make a unique salt for each user? It's easier for a script kiddie to get info from the DB than to read a php file, so why not just attach a global salt in php? Its less likely that they will get into your scripts than you db. Either way, if they get into either, they can do whatever they want, so protecting against someone who finds their way into your DB or scripts is pointless.

I think a lot of effort and processing power gets wasted in this area. I think the time is much better spent in the effort of stopping injections/vulnerabilities than worrying about a password hash.

I recommend you reread some of the earlier posts regarding global salts versus per-user salts - if you are indeed vulnerable to SQL injection, then any script kiddie will be able to easily dump your entire hashed password list. If you use a global salt, then brute-force attempts can be made against the entire list at once. This is very bad. Per-user salts prevent this by forcing attacks to be made against individual passwords. Combine both methods and some script kiddie who dumps your passwords doesn't have the global salt, while a truly skilled Bad Guy(R) who also gets your scripts still has to attack each individual password.

Factor in the fact that generating per-user salts is a trivial exercise that takes only a couple of milliseconds of execution time, and I'm left wondering why anyone would choose not to when they know the security benefits of doing so.

Originally Posted by rjm1982

When i see sql statements like md5(concat(md5('password'),md5(salt))) i cringe. Thats not really -that- much more secure than md5(concat('password'),salt).

I'm wholly behind you here. Except that I would go so far as to say that md5(concat(md5('password'),md5(salt))) is not at all more secure than md5(concat('password'),salt).

Here is how I have chosen to deal with passwords:
Per User Salts
Global Salt
Use non ASCII characters in salt, numbers, letters (uppercase and lowercase), and a few symbols such as dollar sign
use sha512 or sha256 to hash password