1
$\begingroup$

As I understand it, the salt is used to ensure that a hash of two of the same strings results in a different hash.

The salt is often stored with the hash, either prepended or as a separate field.

As this just needs to be "different" for every input, would using something like:

DateCreated.ToString("yyyyMMddHHmmssffffff")

To get a number string down to the millisecond - If you can guarantee that you'd not have two requests in the same millisecond. This would be unique per record and provide the means to change the hash.

As an example, if I run a basic hash:

 using (HashAlgorithm algorithm = SHA256.Create())
                return algorithm.ComputeHash(Encoding.UTF8.GetBytes(inputString));

If my input string is BabySharkDoDoDoDo - every time this is hashed, I'll get the same result: 567d01ee59d062ff75435033b4c2593058aed5b5ccca0f29ba92b22c13e084b3

So, to make sure each entry to my shark database is unique, I prepend the inputString with DateTime.ToString("yyyyMMddHHmmssffffff") - so my input to the hash becomes 202104132059471235487BabySharkDoDoDoDo and each time I get a unique hash.

So I can check the input is valid, if someone enters the right record and the string BabySharkDoDoDoDo I need to store the salt, either with the hash or in the separate column.

So, the salt is known to the attacker - always the case right? So making a super strong salt, with crazy random entropy to then go and store it right next to the data it's salted makes me think, is this wasted effort? Or theatre to seem great but really all you need is a unique piece of data to provide the salt?

If I go for it and create a 128 bit salt, from a crypo library, Rijndael or similar - sure it's unique (hopefully), but I then go and prepend it to the saved hash - right? Otherwise, I can never check the hash against a known input.

A timestamp down to the millisecond or tick would also be unique and serve the same purpose?

$\endgroup$
7
  • $\begingroup$ Are we talking about password hashing? Cryptography hash functions never ensure that the results are different, there is always a probability of collision, that is negligible, however, exits. A prefix string in the hash functions is used for domain separations, SHA3 uses this extensively in the easy way. $\endgroup$
    – kelalaka
    Apr 13, 2021 at 18:43
  • $\begingroup$ Say I'm going to hash a password, using SHA256, if I hash the same string I get the same hash... So I use a salt on the password to ensure that the hash is different. Do I have to be concerned about the randomness of the salt? $\endgroup$
    – RemarkLima
    Apr 13, 2021 at 18:47
  • $\begingroup$ Note that there is UUID that guarantees to be unique, might be this what you are looking for? $\endgroup$
    – kelalaka
    Apr 13, 2021 at 18:47
  • $\begingroup$ Well, in the case of passwords, you need to use password hashing functions like Scrypt, PBKDF2, and Argon2. They can be slowed, made memory-hard, etc. Could you please provide us, your actual problem by editing instead of hiding it? Here, the devil in details! $\endgroup$
    – kelalaka
    Apr 13, 2021 at 18:49
  • $\begingroup$ It's a hypothetical question rather than an actual problem as I just use the out of the box functionality - but PKBDF2 requires a salt... I'll edit with more detail. $\endgroup$
    – RemarkLima
    Apr 13, 2021 at 19:36

3 Answers 3

0
$\begingroup$

Most of your questions have been generally dealt with by Hashing passwords with a salt - why use different salt for everyone? and the salt wiki entry.

All I'll do is simply press home that salting is for use with short/low entropy passwords. It stops people pre-computing hashes of common passwords like secret. And here's the rub with your millisecond scheme. It's partially pre-computable as time is predictable. There's probably some script jockey out there right now pre-computing hashes of the next few years' string formatted time and distributing the list (rainbow table) on the internet. Hence random/partially random salts.

And as mentioned otherwise, your computer (/dev/urandom) is full of them, and they're free.

$\endgroup$
3
  • 1
    $\begingroup$ Thanks, this makes the most sense in that someone will be pre computing commons passwords with a timestamp salt for the next year and you risk a hit everytime! This addresses the "why" :-) $\endgroup$
    – RemarkLima
    Apr 13, 2021 at 21:57
  • 1
    $\begingroup$ I would be surprised that one builds a Rainbow table per salt yet a strong password like constructed dicewire is far from attackable from the Rainbow tables. $\endgroup$
    – kelalaka
    Apr 13, 2021 at 22:18
  • 3
    $\begingroup$ It's not a problem if the salt is predictable. It allows attackers to pre-compute values, but not to do less computation. What's a problem is if the same salt is reused, for example if different servers all use timestamps in the same format. That does allow attackers to attack many servers with the same precomputed values, and therefore can make precomputing values useful for the attacker. $\endgroup$ Apr 14, 2021 at 11:15
3
$\begingroup$

You're going out of your way to create more work for yourself, and introducing a vulnerability. It's not necessarily a huge vulnerability, but why bother?

To get a number string down to the millisecond - If you can guarantee that you'd not have two requests in the same millisecond.

You cannot have such a guarantee, not without doing very complicated things.

  • If your server is fast enough, it may take less than one millisecond to serve a request. Even if it doesn't now, maybe it will after a hardware upgrade.
  • Clocks are not always accurate. Just because you have a function GetCurrentTimeInMilliseconds() doesn't mean that it has millisecond accuracy.
  • The time can go backward if the clock is adjusted. Or if you use the local time rather than wall-clock time.
  • If you have multiple threads running on multiple processors, they will, from time to time, happen to serve a request at exactly the same time, no matter how much resolution your clock has.
  • If the same code is running on multiple servers, it's the same thing: they'll all be generating the same number at the same time.

Depending on the purpose of the salt, repeating a value may or may not be catastrophic. For a nonce or IV in encryption, it is often catastrophic. For password hashing, the attacker only has a small advantage if the salt is rarely repeated.

then go and prepend it to the saved hash - right? Otherwise, I can never check the hash against a known input.

Yes. This applies no matter how you generated the salt.

So making a super strong salt, with crazy random entropy to then go and store it right next to the data it's salted makes me think, is this wasted effort? Or theatre to seem great but really all you need is a unique piece of data to provide the salt?

You don't need “crazy random entropy”. You just call GenSecureRandom() or whatever your function to generate cryptographically secure random bytes is called. This is less complicated than the date-based approach you propose!

Even better, when hashing a password, the password hashing library should take care of generating the salt. But if you're stuck with an old API that requires you to do the work, then just generate a random salt of the length stated in the documentation of the library or of the password hashing algorithm. This is the simplest method.

$\endgroup$
7
  • $\begingroup$ OK, the hypothetical question was that you can 100% guarantee that the timestamp is unique, and calling a date to string format isn't "hard" and may already exist in the database / method. PBKDF2 requires you to add the salt and iterations and you want this as a slow method in the case of passwords... But if you can guarantee uniqueness of the salt, a padded database ID perhaps, this still doesn't cover "why" the salt needs to be random. $\endgroup$
    – RemarkLima
    Apr 13, 2021 at 22:02
  • 1
    $\begingroup$ @RemarkLima It doesn't need to be random, but it needs to be unique. The hypothesis that the timestamp can be guaranteed to be unique is wrong. It's not hard to get a usually-but-not-always unique timestamp, but it's not easier than getting a random string, and it is hard to get a guaranteed-unique timestamp. $\endgroup$ Apr 14, 2021 at 10:24
  • 1
    $\begingroup$ @RemarkLima It needs to be unique. Randomness is an easy way to make it unique, but not a requirement in itself. It's hard to beat randomness for getting a guaranteed unique value in a simple way (when the value is long enough, which is the case for a password hash salt). $\endgroup$ Apr 14, 2021 at 10:35
  • 1
    $\begingroup$ @RemarkLima My answer doesn't say “no, the salt must be random”. It says “no, your method is not a simpler way of getting a unique salt”. $\endgroup$ Apr 14, 2021 at 10:36
  • 1
    $\begingroup$ @RemarkLima For password hashing, it's not a problem if the salt is predictable. $\endgroup$ Apr 14, 2021 at 11:12
-1
$\begingroup$

As I understand it, the salt is used to ensure that a hash of two of the same strings results in a different hash.

In short yes.

Remember, the hash functions are not producing unique outputs per input. This can be simply seen by the pigeonhole principle. The input of the hash functions are arbitrary binary strings and the output is fixed length of $b$.

$$H:\{0,1\}^*\to \{0,1\}^b$$

Therefore the collisions are inevitable and the generic cost of finding one is $\mathcal{O}(2^{b/2})$ with 50% probability by the birthday attack.

Therefore, even with the salt (prepended or appended) the collision will be the same.

This would be unique per record and provide the means to change the hash.

Yes, we expect that cryptographically hash functions have avalanche property so that with changing 1 bit we expect that the output bit flips with 50% probability.

So, the salt is known to the attacker - always the case right? So making a super strong salt, with crazy random entropy to then go and store it right next to the data it's salted makes me think, is this wasted effort? Or theatre to seem great but really all you need is a unique piece of data to provide the salt?

Yes, we assume that the attacker has the salts.

Entropy is the property of the process not a property of strings. We prefer the word strength like the password strength.

Without the salt hash functions (as in hashed passwords) have a generic pre-image attack case; Rainbow tables to find the pre-image. These tables speed up the search if more than one search is going to be performed. The salt simply kills the Rainbow tables, since the attacker needs to build one table per salt. Therefore, with the salt they become useless.

To prevent the reuse of the salt one should prefer 128-bit salt value and this is a recommendation of the NIST.

If I go for it and create a 128 bit salt, from a crypo library, Rijndael or similar - sure it's unique (hopefully), but I then go and prepend it to the saved hash - right? Otherwise, I can never check the hash against a known input.

Thanks to Microsoft RijndaelManaged, we are still seeing the Rijndael around. Rijndael was standardized as AES with some changes like the fixed block size 128 and key sizes as 128,192,256 where the middle size is used by nobody!

A block cipher is a family of permutations and if you fix a key, then you will get a permutation. Then for each user you can encrypt user ID, 1,2,3, etc., or their unique some other information. In this way, the salt will be different per user.

For a different server, select a new random key.

A timestamp down to the millisecond or tick would also be unique and serve the same purpose?

Yes, as long as there is no multi-process and resolution of the time ticks are good enough you can use it. Too many ifs?

If the time has subject to change or the resolution is not enough then/or better use /dev/urandom to get a 128-bit salt value.

If you are using multiple servers then you can use an additional pepper string to separate the domains of the servers.

$\endgroup$
3
  • $\begingroup$ There's a lot of correct and interesting, but irrelevant information about password hashing. However, your recommendation to use a clock value or an encrypted value as the salt is bad. Neither is unique over users and servers. An encrypted value means an extra key to store and protect. Both are more complicated than the normal method of generating a random value, for no benefit. $\endgroup$ Apr 13, 2021 at 20:53
  • $\begingroup$ @Gilles'SO-stopbeingevil' I've used the password between the commas, to ease the similarity of finding pre-images. There is no need to store the key secretly, it is suggested to use it as a permutation and a different key can be used per server. I did not consider multiple servers, there the solution is the pepper. I suggest using nanoseconds since I did not consider multiple servers/process and warned that, too. $\endgroup$
    – kelalaka
    Apr 13, 2021 at 21:05
  • $\begingroup$ @Gilles'SO-stopbeingevil' addresses the issues. $\endgroup$
    – kelalaka
    Apr 13, 2021 at 21:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.