1

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?

RemarkLima
  • 113
  • 5

3 Answers3

3

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.

0

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.

Paul Uszak
  • 15,905
  • 2
  • 32
  • 83
-1

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.

Aman Grewal
  • 1,421
  • 1
  • 10
  • 24
kelalaka
  • 49,797
  • 12
  • 123
  • 211