59

I'm looking at some code that I have not written myself. The code tries to hash a password with SHA512 and uses just time() as the salt. Is time() too simple a salt for this or is this code safe?

Thanks for the answers and comments. I will sum it up here for the new readers:

  • salt should be different for each user, so if 2 users register at the same time, their salts won't be unique. This is a problem, but not a big one.
  • but salt shouldn't be in any way related to the user, so time() is not a good salt.
  • "Use a random, evenly distributed, high entropy salt." -- That's a mouthful, so what code could possibly generate a random, evenly distributed, high entropy salt?

Ok, so how about I replace time() with a random string 32 char long. The random string could be generated from looping 32 times over a set of alphabet chars. Does that sound good?

Amal Murali
  • 75,622
  • 18
  • 128
  • 150
zmol
  • 2,834
  • 5
  • 26
  • 29
  • 11
    I recommend using a two part salt. One unique per user salt. And one that's stored in a config file and kept secret. That way an attacker needs access to both the filesystem and the database, and not only the database. – CodesInChaos Feb 13 '11 at 12:17
  • on a unix based system, you probably have access to /dev/random otherwise, use you could... hmm, this is a good questions that deserves it's own thread. – Jacco Feb 13 '11 at 12:30
  • @Your last question: `mt_rand` isn't cryptographically strong, but it will give you much more "randomness" than `time`. PHP currently doesn't provide a way to get cryptographically strong random numbers (but this is being worked on). So you should either stick to `mt_rand` or fork `/dev/(u)random`. – NikiC Feb 13 '11 at 12:34

9 Answers9

87

Short answer:

No, time() is not a good salt.

Long answer:

copied from my answer to Salt Generation and open source software

What is a salt?

A salt is a random set of bytes of a fixed length that is added to the input of a hash algorithm.


Why is salting (or seeding) a hash useful?

Adding a random salt to a hash ensures that the same password will produce many different hashes. The salt is usually stored in the database, together with the result of the hash function. Salting a hash is good for a number of reasons:

  1. Salting greatly increases the difficulty/cost of precomputated attacks (including rainbow tables)
  2. Salting makes sure that the same password does not result in the same hash. This makes sure you cannot determine if two users have the same password. And, even more important, you cannot determine if the same person uses the same password across different systems.
  3. Salting increases the complexity of passwords, thereby greatly decreasing the effectiveness of both Dictionary- and Birthday attacks. (This is only true if the salt is stored separate from the hash).
  4. Proper salting greatly increases the storage need for precomputation attacks, up to the point where they are no longer practical. (8 character case-sensitive alpha-numeric passwords with 16 bit salt, hashed to a 128 bit value, would take up just under 200 exabytes without rainbow reduction).


There is no need for the salt to be secret.

A salt is not a secret key, instead a salt 'works' by making the hash function specific to each instance. With salted hash, there is not one hash function, but one for every possible salt value. This prevent the attacker from attacking N hashed passwords for less than N times the cost of attacking one password. This is the point of the salt.
A "secret salt" is not a salt, it is called a "key", and it means that you are no longer computing a hash, but a Message Authentication Code (MAC). Computing MAC is tricky business (much trickier than simply slapping together a key and a value into a hash function) and it is a very different subject altogether.

The salt must be random for every instance in which it is used. This ensures that an attacker has to attack every salted hash separately.
If you rely on your salt (or salting algorithm) being secret, you enter the realms of Security Through Obscurity (won't work). Most probably, you do not get additional security from the salt secrecy; you just get the warm fuzzy feeling of security. So instead of making your system more secure, it just distracts you from reality.


So, why does the salt have to be random?

Technically, the salt should be unique. The point of the salt is to be distinct for each hashed password. This is meant worldwide. Since there is no central organization which distributes unique salts on demand, we have to rely on the next best thing, which is random selection with an unpredictable random generator, preferably within a salt space large enough to make collisions improbable (two instances using the same salt value).

It is tempting to try to derive a salt from some data which is "presumably unique", such as the user ID, but such schemes often fail due to some nasty details:

  1. If you use for example the user ID, some bad guys, attacking distinct systems, may just pool their resources and create precomputed tables for user IDs 1 to 50. A user ID is unique system-wide but not worldwide.

  2. The same applies to the username: there is one "root" per Unix system, but there are many roots in the world. A rainbow table for "root" would be worth the effort, since it could be applied to millions of systems. Worse yet, there are also many "bob" out there, and many do not have sysadmin training: their passwords could be quite weak.

  3. Uniqueness is also temporal. Sometimes, users change their password. For each new password, a new salt must be selected. Otherwise, an attacker obtained the hash of the old password and the hash of the new could try to attack both simultaneously.

Using a random salt obtained from a cryptographically secure, unpredictable PRNG may be some kind of overkill, but at least it provably protects you against all those hazards. It's not about preventing the attacker from knowing what an individual salt is, it's about not giving them the big, fat target that will be used on a substantial number of potential targets. Random selection makes the targets as thin as is practical.


In conclusion:

Use a random, evenly distributed, high entropy salt. Use a new salt whenever you create a new password or change a password. Store the salt along with the hashed password. Favor big salts (at least 10 bytes, preferably 16 or more).

A salt does not turn a bad password into a good password. It just makes sure that the attacker will at least pay the dictionary attack price for each bad password he breaks.


Usefull sources:
stackoverflow.com: Non-random salt for password hashes
Bruce Schneier: Practical Cryptography (book)
Matasano Security: Enough with the Rainbow Tables
usenix.org: Unix crypt used salt since 1976
owasp.org: Why add salt
openwall.com: Salts

Disclaimer:
I'm not a security expert. (Although this answer was reviewed by Thomas Pornin)
If any of the security professionals out there find something wrong, please do comment or edit this wiki answer.


As for what seems to be a good source for your random salt
Also read: What is the most secure seed for random number generation?
In the absence of dedicated, hardware based, random generators, the best way of obtaining random data is to ask the operating system (on Linux, this is called /dev/random or /dev/urandom [both have advantages and problems, choose your poison]; on Windows, call CryptGenRandom())

If for some reason you do not have access to the above mentioned sources of random, in PHP you could use the following function:
From the source of phpass v0.3

<?php
/**
 * Generate pseudo random bits
 * @copyright: public domain
 * @link http://www.openwall.com/phpass/
 * @param int $length number of bits to generate
 * @return string A string with the hexadecimal number
 * @note don't try to improve this, you will likely just ruin it
 */
function random_bits($entropy) {
    $entropy /= 8;
    $state = uniqid();
    $str = '';
    for ($i = 0; $i < $entropy; $i += 16) {
        $state = md5(microtime().$state);
        $str .= md5($state, true);
    }
    $str = unpack('H*', substr($str, 0, $entropy));
    // for some weird reason, on some machines 32 bits binary data comes out as 65! hex characters!?
    // so, added the substr
    return substr(str_pad($str[1], $entropy*2, '0'), 0, $entropy*2);
}
?>
Community
  • 1
  • 1
Jacco
  • 23,534
  • 17
  • 88
  • 105
  • ok, so how about I replace time() with a random string 32 char long. The random string could be generated from looping 32 times over a set of alphabet chars. Does that sound good? – zmol Feb 13 '11 at 12:33
  • You should block-quote that. And consider making it a community wiki as just quoting another answer is not that big output. – Gumbo Feb 13 '11 at 12:38
  • 2
    It might be worth noting a new function in the (standard) OpenSSL extension in PHP 5.3: [`openssl_random_pseudo_bytes()`](http://php.net/openssl_random_pseudo_bytes) as a fast, effective, well-tested entropy generator. – Charles Feb 22 '11 at 07:13
  • 2
    The long answer doesn't directly address why a timestamp is a bad choice. Reading through, it would appear the only issue with timestamp in general is that it is predictable. An attacker could create a table that targeted all users created at particular time but that's only a problem if several users are created at the same time. If that's not the case, then it appears the timestamp just as good as a random salt*. Then the real answer appears to be 'There is no need to because its trivial to get a random value that would prevent even that attack'? *Though the php time() specifically may not be – jmathew Apr 16 '18 at 18:03
  • A potential downside is that if it's possible for someone to fiddle with your computer's time they could make your service generate a single timestamp negating your salt. Like say through a malicious NTP server. Still very narrow implications (compromised computer, before user creation). – jmathew Apr 16 '18 at 18:11
  • time is a good salt if u use hour+minute+second+millisecond+microsecond+nanosecond – clockw0rk Oct 01 '18 at 10:03
  • @jmathew if your computer is compromised before you even create an account, you have other problems than a salt ;) – clockw0rk Oct 01 '18 at 10:04
  • 2
    @clockw0rk No, time is *never* a good salt, because time is not evenly distributed. Because of this, attackers only need to attack a subset of all possible values. – Jacco Oct 01 '18 at 10:05
  • @Jacco not trying to showoff, but I happen to have debugged a lot of random-stuff in c - and the srand - function takes program millies as kind of a salting value - there is not much difference to this question. You could multiply 2 timestamps with a randomInt for more entropy tho, but in the end every random will be predictable, won't it? also, if you used nanoseconds, you would almost alwys get a very different value than the last time, I was not really talking about the year and month part of the timestamp. have a nice day – clockw0rk Oct 01 '18 at 10:22
  • If you think you are smarter than the best practices, be my guest. If you want to learn why you solution is not a good idea, ask a question at https://security.stackexchange.com/ – Jacco Oct 01 '18 at 11:34
  • So using user id, if user id is a random UUID (v4), is a perfect choice of salt. Plenty of entropy, random, and unique. – Eloff Apr 14 '21 at 13:01
2

Updated

It's not a really good salt, but probably good enough to defeat all but the most determined and resourceful attackers. The requirements for a good salt are:

  • Different for each user
  • long enough (at the very least alphanumeric 8 characters) to make the concatenation of salt and (potentially weak) password too long for a brute force attack.

time() values are not really long enough, since they have 10 characters, but only digits.

Also, sometimes two users may get the same value when they are created within the same second. But that's only a problem if you have situations where many users are automatically created within the same second.

In any case, far more important than a perfect salt is using a good hash function, and SHA512 is one of the best we have available right now.

Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
  • 3
    Another requirement is, that the salt isn't in any way related to the user. Thus `time()` isn't ideal, as you already said. – NikiC Feb 13 '11 at 11:58
  • 2
    Where the length of 8 chars came from? – zerkms Feb 13 '11 at 11:58
  • You missed one criterium: non-predictable. In other words, time is not a good salt. – Jacco Feb 13 '11 at 12:11
  • Not a good salt. One attack vector is based on forensically determining what time the data was encrypted. – RichardTheKiwi Feb 13 '11 at 12:14
  • 1
    @Jacco Why is unpredictability that important for a salt used in password hashing? The attacker knows the hash since it is stored in the database together with the hash. – CodesInChaos Feb 13 '11 at 12:16
  • 1
    @cyberkiwi: attacker knows the salt, so what? There is no way to do anything but bruteforce in this case anyway. – zerkms Feb 13 '11 at 12:18
  • Because time() is a clustered result. I know that more users are only during daytime than during nighttime. So building my precomputation attack takes less time as I can achieve a higher chance of succes by predicting that the big chunk of user-passwords where created during daytime. (the downvote isn't mine) – Jacco Feb 13 '11 at 12:20
  • @Jacco I updated the question to include a summary of the answers, and requesting clarification about what you said `Use a random, evenly distributed, high entropy salt.` See my updated question – zmol Feb 13 '11 at 12:23
  • 1
    @Jacco: You don't need to predict anything, you *know* the salt for each user. The clustering is no problem at all unless you get a situation where a large number of users is automatically created within the same second. Or, of course, if your hash function has weaknesses. – Michael Borgwardt Feb 13 '11 at 12:26
  • @zerkms: around 8 characters is where brute force attacks start getting really expensive, so you want your salt to push even weak/dictionary passwords well over that limit. But now I realize that time() only produces digits, making it decidedly too weak. – Michael Borgwardt Feb 13 '11 at 12:28
  • @Michael Borgwardt ok, so how about I replace time() with a random string 32 char long. The random string could be generated from looping 32 times over a set of alphabet chars. Does that sound good? – zmol Feb 13 '11 at 12:32
  • @Michael Borgwardt the world is bigger than 1 system alone. The power of precomputation attacks is in re-usability, across systems. There is just no reason not to use a random salt and sidestep the entire problem. – Jacco Feb 13 '11 at 12:33
  • @zmol: not sure I understand your method, but 32 alphanumeric characters would definitely be enough. – Michael Borgwardt Feb 13 '11 at 12:35
  • 1
    @Michael Borgwardt: uhm, longer salt make the hash evaluation longer?! Are you sure? "making it decidedly too weak." --- Why? sha wasn't compromised, so there is no way to hack it by any intelligent way, only bruteforce (and it doesn't matter what the salt is in this case). Am I wrong? – zerkms Feb 13 '11 at 12:52
  • @Jacco rainbow tables are large enough for even non-salted values. If you need to calculate rainbow table for every salt in the world, this is technically not feasible. IOW there's no practical way for reuse of calculations. Also, as others said, salt is known or can be learned, so how random it is, doens't matter. – Eugene Mayevski 'Callback Feb 13 '11 at 13:21
2

This post may veer a little too far away from your original question, but I hope you find it useful;

Security is about raising barriers and hurdles; defence in depth. There is no truly secure hashing solution, just ones that are hard to break. It's like putting in a burglar alarm and window locks in your house - make your site less attractive to break into than someone else's.

Salt for a crypt algorithm is only a small part of the security problem. A single salt simply means that there is one less thing to figure out when trying to break the password for multiple users. A low-entropy salt (such as the server's time) makes it a little bit harder, and a high-entropy salt makes it harder still. Which of these to use, and whether it's something you need to worry about primarily depends upon both the sensitivity of the data you're protecting, but also what other security measures you have in place. A site that just gives a personalised weather forecast for a selected city obviously has less sensitive data than one which has your home address, mother's maiden name, date of birth and other info which could be used for identification purposes.

So here's the rub; a high entropy salt is still a bad salt if it's easily obtainable.

In the real world, storing a salt in the database (random or not) is probably less secure than using a constant salt and burying it away from private eyes in a file inaccessible via the web browser. Whilst a unique and high entropy salt is harder to guess, if you've allowed root login from any server on MySql and set the password to 'password' it doesn't really matter! Constrast how easy it is to crack the database versus getting a valid login to your server - which is possibly more difficult to do discretely as you can put fail2ban and a plethora of other attack vector watchers in place depending upon your setup.

You can combine the two approaches by storing the location of a file containing a user-specific salt in the database, rather than the salt itself. Whether having to crack both the file system and the database is warranted depends whether the sensitivity of the data you are trying to protect warrants this overhead.

Another, alternative, recommendation from security experts is to store the username in a separate database (and ideally different technology) to the password, and reference between the two using a UUID. E.g. use both MySQL and SQLite. This means that both databases have to be cracked (and is also why, to go down a separate rabbit hole for the sake of an example, you should not store user details and credit card numbers in the same database since one is of no use without the other).

Note that Algorithms like SHA-512 and Blowfish can return the salt as part of their hash. Be careful with these as if you store the complete hash you give away the algorithm, which means there's two less thing for the hackers to figure out (the salt also gives away the algorithm).

Make sure you enforce strong passwords and usernames, so dictionary attacks will fail; I know of dictionaries for all 6-alphanumeric combinations of username/ password entries for MD5 and I suspect that there are more than this available for all sorts of algorithms. With the explosion of low-cost cloud and CPGPU computing, the size and complexity of available dictionaries is going to explode.

Ultimately, the most secure way is never to programatically generate a salt but require a user to enter it along with their username and password over a SSL link (so can't be snooped), but never store it. This is the approach taken by credit card companies; i.e. the 3-digit CSV security key on your credit card which you have to enter each and every time you buy online, since it should never be stored in any database. If you really want to generate the salt, send it to them separately (e.g. via SMS message or Email) and still make them enter it manually each time. With this approach, although more secure, you need to contrast the complexity against whether users will just stop using the site as you've made it too difficult for them to be bothered with it.

All of the above still relies on the fact that you also have protection in place against session hijacking, cross-site scripting, etc., etc. The world's strongest password algorithm is irrelevant if all I need to do is to calculate a valid PHPSESSID for a logged-in user and hijack it!

I am not a security expert, but have read up on this as much as I reasonably can do. The fact that there are so many books on the subject indicates how big the answer to your question really is.

A couple of really great books you might like to try which I've found invaluable are;

Web Application Vulnerabilities Detect, Exploit, Prevent - ISBN-13: 978-1-59749-209-6

Preventing Web Attacks with Apache - ISBN-13: 978-0-321-32128-2

Matt
  • 453
  • 3
  • 5
  • 3
    Although in general your answer is correct, some of the statements are plain false: "a high entropy salt is still a bad salt if it's easily obtainable." This statement, for example, is simply not true. – Jacco Feb 14 '11 at 09:22
1

Yes.
It seems that a unix timestamp, stored in the user database as a "Member since" field going to be decent salt.

However, salt question is most negligible one. There are much more important things you have to pay attention to:

  1. Most likely not a password nor salt or hashing algorithm going to be weakest part of your site. Some lame file injection or XSS or CSRF surely is. So, don't make a too big deal of it.
    Speaking of a true random string of 32 char long in the typical web-application is like speaking about 32-inch armored door in the wooden barn.

  2. Speaking of passwords, most ever important thing is password complexity. With weak password no salt nor hashing algorithm, even super-ingenious-incredible-hard one, could help. It's a pain to ask users to use complex password, but without it everything else becomes a piece of crap.
    So, your first concern should be password complexity. 12-16 characters of different case, including numbers and punctuation is a requirement.

  3. As for the salt, I see no benefit in using time, as you have to store it along with other user data. Better use a email - it's random enough and you have it already anyway. Don't forget to rehash a password if user changes their email. it seems that unix timstamp going to be a decent salt, no need to use email or anything else.

Update
As I can see, many people still unable to get the point.
Like that guy from the comments, saying

Many users use weak passwords (we should educate them, or at least keep trying), but that is no excuse; they still deserve good security

They deserve, no doubt. But with weak password the mission. is. impossible.

If your password is weak, then no salt will protect it.

While salt is not that important to spend a 10-kilobyte text on the topic.

Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
  • 13
    Many websites don't protect anything of value. But since users tend to reuse passwords a lot, the passwords themselves become the most valuable set of data typically available on those website. So in my opinion, properly securing the passwords is important, whatever the situation. Many users use weak passwords (we should educate them, or at least keep trying), but that is no excuse; they still deserve good security. Also, using a random salt is easy to implement; advising something else is just advising others to stop caring. – Jacco Feb 18 '11 at 11:05
  • I think this answer gets a lot of things wrong. a) Password complexity is not anywhere as important as password length. Having punctuations in a password is more harm than it is good. b) passwords, for most trivial sites, are the most important asset a site has. c) Even with a weak password, there are plenty of things you can do to enhance its security. However, I do agree that no salt in itself will protect a weak password. You need at least a proper hashing strategy as well. – eis Apr 24 '14 at 15:49
  • Where is the harm in having punctuations in a password? @eis – René Roth Apr 24 '14 at 16:11
  • 1
    @RenéRoth you're far less likely to remember them correctly in the long term, so the likelihood that you'll write them down increases. – eis Apr 24 '14 at 16:39
1

No, time() is not a good salt

It's best not to reinvent the wheel when it comes to authentication, but to answer your question, no. The problem with time():

  • It's predictable and it correlates to potentially discoverable things. These issues make it easier to cross-match different hashed results.
  • There aren't very many possible values. Since the high-order bits don't change, it's an even narrower salt than it first appears.
  • Using it repeats previous mistakes. If this app were the first one to use time() as a salt, at least it would require a new attack.
DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
0

Salt is use to prevent rainbow attacks by breaking the match between the password and precomputed hash. So the main task for a salt is to be different for each user/password record. Quality of randomization of the salt doesn't matter much as long as the salt is different for different users.

Eugene Mayevski 'Callback
  • 45,135
  • 8
  • 71
  • 121
0

the date when a member joins a forum/website is generally openly access able , which would be same as time() hence making your salt useless.

Mr Coder
  • 8,169
  • 5
  • 45
  • 74
0

No! Never use the current time as the salt. You can use something like 'SecureRandom' in java to generate a random salt that is secure. Always use an unpredictable random number as the salt.
Using time as the salt will help you to remove collisions only upto a certain extent(because two users can sypply the same passwords at the same time), but still make the passwords recoverable.

Ashwin
  • 12,691
  • 31
  • 118
  • 190
-2

The user name should be sufficient enough and perhaps the registration time stamp, but you should store it somewhere in the database. Anyway every value you use to salt your password hash, should be stored some way, so you can recalculate the hash.

Is salting with user name + a time stamp secure enough? It should be. For cracking SHA512 Hashes normally Rainbow Tables are used. A user name + a time stamp should be a salt which is uniquq enough, so there is no way there is some Rainbow Table on the net which contains precalculated hashes with passwords, which are salted this way.

kalkin
  • 870
  • 1
  • 8
  • 15
  • I over read that he wants to hash passwords. Like i said the username and perhaps the registration timestamp should be enough. – kalkin Feb 13 '11 at 11:59