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?
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.
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
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.
Short answer:
No,
time()
is not a good salt.Long answer:
copied from my answer to Salt Generation and open source software
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, callCryptGenRandom()
)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
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.
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():