14

Alright, so I understand that users are the kind of beasts who like to use one password and make it short and easy to remember (like "doggies"). If I understand correctly, that's one reason we use password salts. They add length and some level of randomness. My problem is how to generate them. The way I've been generating my salts (and the password hashes) in a recent (still entirely in-house) project is to do the following:

  1. Grab the time in seconds since the epoch
  2. Grab the time the process has been running (as granular as the OS allows)
  3. Create the MD5 hash of both results above
  4. Append the 4 strings I now have in a varying combination
  5. Store this as plaintext
  6. Append the password and salt in a few ways
  7. SHA-512 hash that to create the password hash

Is this sufficiently random? Does it add enough entropy? I recently read I need a stronger hashing algorithm (looking at bcrypt) and need multiple hashes (1000+) to be safe enough, but that's not the focus of the question.

Nakaan
  • 255
  • 1
  • 6
  • 1
    one thing to note is that algorithms like bcrypt should handle the complexity around salts and hash interation for you. you can specify a work factor to increase the interations. for python there's some more info. here http://www.mindrot.org/projects/py-bcrypt/ – Rory McCune Dec 12 '12 at 09:55

2 Answers2

17

This seems misguided.

Time in seconds from any source lacks almost any entropy, and is almost immediately guessable. If your OS allows microsecond granularity for a running process, you'll get 4 or 5 bits of entropy there, from the lower bits, but a lot less if someone finds a way to get the exact system uptime.

You're then talking about MD5, which isn't really useful in this case. All it does it give you the impression that you've made it more random, without actually making it random. Really you've only got a couple of integers that are dependant on each other (process time is always going to be in sync with system uptime) and that are entirely non-random. Your system will almost certainly leak its uptime in one form or another. You'd actually be better off using an LCG like PHP's rand(), since the internal state can't be guessed as easily.

This approach to random number generation is bad, because it's not random at all, and can be predicted. However, salts don't actually need to be particularly random, they just need to be unique; they're only there to prevent rainbow table attacks. Unpredictability helps (see "how to store salt?") but isn't entirely required. Regardless, there are some benefits to making the salt difficult to predict.

If you're on Linux, read some bytes from /dev/urandom to generate the salt - you don't need to do anything more than take those bytes and use them, because they're already strong random numbers.

If not, you could generate the salt as SHA256(username || email || time || mt_rand()) where || denotes concatenation, time is the current system time in as high a resoluton as you can get, and mt_rand() is an output value from whatever random generator is available in your programming language's default library. In PHP you should use mt_rand rather than rand, since it has better characteristics. This provides enough entropy for a salt to be very difficult to guess remotely, especially given the limited opportunities for re-use of username and email combinations.

For storing the password, you should definitely be using PBKDF2 or bcrypt. A single use of a cryptographic hash function is simply not enough to protect you from bruteforce attacks, especially when almost everyone has an OpenCL-compatible GPU these days.

Polynomial
  • 132,208
  • 43
  • 298
  • 379
  • 1
    Are you using || as OR or concatenation? – Nakaan Dec 12 '12 at 07:07
  • Concatenation. Sorry, should've specified that. In general for security, though, you'll see people use `||` to mean concatenate, and `|` to mean a bitwise OR operation. – Polynomial Dec 12 '12 at 07:09
  • Ah, okay. I would have expected + or . haha. Anyway, I'm building this in Python (with the planned release to be Windows) and have never looked into the random number generation facilities of it (no need until now). Salts ARE stored in plaintext, right? – Nakaan Dec 12 '12 at 07:12
  • 1
    Yup, store them in plaintext. No need to obfuscate them, since they're not really meant to be secret information (though it helps in certain cases; see the answer I linked). They're just there to make pre-computed rainbow table attacks infeasible. – Polynomial Dec 12 '12 at 07:14
  • Just an aside - here's a short story about secret salts from [RFC 5869](http://tools.ietf.org/html/rfc5869): It is worth noting that, while not the typical case, some applications may even have a secret salt value available for use [...]. An example of such application is IKEv1 in its "public-key encryption mode", where the "salt" to the extractor is computed from nonces that are secret; similarly, the pre-shared mode of IKEv1 uses a secret salt derived from the pre-shared key. – Henning Klevjer Dec 12 '12 at 07:15
  • Cool. Please excuse my lack of knowledge, I'm a complete noob to cryptography (despite a significant interest) Thank you for the help! – Nakaan Dec 12 '12 at 07:23
  • @Nakaan - As Polynominal said, a salt is not meant to be secret, it fullfills it's purpose to thwart rainbow tables even when known. If you want to add a secret, then combine your password with a **pepper** additionally to the salt. – martinstoeckli Dec 12 '12 at 11:50
  • @martinstoeckli You're quickly heading into murky territory there. A pepper provides a benefit when the attack vector is purely SQL injection (i.e. database only), but does nothing to protect you if the attacker can read your script files. – Polynomial Dec 12 '12 at 11:57
  • @Polynomial - Exactly, the pepper provides the same advantage, as making the salt secret. As long as the attacker has only access to the database (SQL-injection) it is an advantage, if he has control over the server, he can get the pepper or has access to the hidden salts. I only wanted to propose, that we let the salt doing it's job, and don't mix it up with the job of the pepper. – martinstoeckli Dec 12 '12 at 12:12
  • @martinstoeckli If the developer does his job correctly (i.e. uses parameterised queries) then the SQL injection vector is moot. – Polynomial Dec 12 '12 at 12:45
  • Regarding `SHA256(username || email || time || mt_rand())` note that the initial seed for `mt_rand()` is usually time and pid, which means it adds only very little additional entropy, especially considering that username and email of users are often publicly known. – Lie Ryan Dec 12 '12 at 13:03
  • @LieRyan Agreed, but it doesn't need a lot of entropy, it just needs to be hard to guess. Using request time or current system time alone isn't difficult enough to guess, so the introduction of an LCG (or similar) increases the difficulty. – Polynomial Dec 12 '12 at 13:16
  • @Polynomial - Absolutely agree with you (though it is a very big IF), only meant that if somebody wants to add a secret, he should not "misuse" the salt. – martinstoeckli Dec 12 '12 at 13:23
2

Actually, a salt needs not be random; it needs to be unique. This is worldwide uniqueness (unique for all times and for all servers around the world), and the easiest way to achieve uniqueness is to use randomness: generate 16 bytes (or more) with a cryptographically strong random generator, and the probability that you hit a salt value which has already been used elsewhere is sufficiently small to be neglected.

Randomness is hard. What you present is the combination of a homemade weak random number generator, and a homemade weak password hashing mechanism. The proper way to generate a salt is to ask the system pseudorandom number generator to do it for you (/dev/urandom on Unix-like systems, CryptGenRandom() on Windows, System.Security.Cryptography.RNGCryptoServiceProvider on .NET...). Then, use the salt with a proper password hashing system like bcrypt or PBKDF2. Most bcrypt implementations will actually do the salt generation for you, so that's even better.

Thomas Pornin
  • 320,799
  • 57
  • 780
  • 949