Answering your question
If an attacker has access to a copy of my users database table containing each salt and the related salted password, I can't understand how a CSPRNG would be more secure than a SHA12 hashed UUID. Can someone elaborate?
I’m not sure if you only have read the question “Cryptographically Secure Pseudo-Random Number Generator in Qt/C++ (Cross platform)” at StackOverflow, or if you also noticed the accepted answer. The answer practically answers your question here, just like it answered the question at StackOverflow.
Quoting the accepted answer:
If you think you need a cryptographically secure PRNG for generating salts then I must tell you that you do not understand what the salt is and how and why it works and against which kinds of attacks it is useful.
The simple fact that the salt must be stored in plaintext alongside the hash of the salted password should have given away that you do not really need a cryptographically secure PRNG for salt - or any PRNG for that matter. Frankly, you could have a simple 64-bit number, which you increase by one every time you need a new salt and it would be just as secure as a salt generated by a cryptographically secure PRNG.
Even though that answer was posted back in 2012, things haven’t changed and it is still valid.
That is why CodesInChaos already commented correctly…
Salts only need uniqueness, so a UUID is fine. A CSPRNG is just the simplest way to generate a salt, not the only one.
If you prefer a UUID that's perfectly fine, though I don't know why you'd want to implement password hashing on the database side instead of the application. In general I recommend using higher level APIs which take care of hashing and salting at the same time. e.g. php's
In the unlikely case you’re still unsure about salts and what exactly the reason for their existence is, I would like to point you to the question “Can you help me understand what a cryptographic “salt” is?“ and its accepted answer, which explains things using different wording. Quoting part of the accepted answer which seems to be the most relevant to your request for elaboration:
A salt is simply added to make a common password uncommon. A salt value is generated at random and can be fairly small, the only purpose is to lower the probability that the hash-value will be found in any pre-calculated table. A common way to combine the salt and the password is to simply concatenate them, i.e. the stored hash value is Hash(salt||password). The common password password1 now magically becomes, e.g., 6$dK,3password1 and is very unlikely to be found in a table.
The salt can be stored completely in the clear in the database, next to the hashed value. Once the attacker has the database and wants to find the passwords, he needs to generate the pre-calculated table for each salt individually, a costly operation.
Besides that Q&A, you might want to search Crypto.SE for Q&As related to “salt” which might help you to fully grasp things related to salts. One of many examples: “Why not encrypt salt?”
Diving into your comments
I'm asking because I disagree it is the simplest. MySQL, for example, doesn't have a native CSPRNG.
Then you could use a simple incrementing integer, or anything else that would produce a UNIQUE salt. (Please note the word “unique” has been capitalized as a hint, not to represent frowned-upon “online shouting”.)
There are several reasons. First, database servers are way more secure than web servers. They are, most of the time, only accessible through the application.
First off, I have to disagree with your
database servers are way more secure than web servers because that is not really the case. But let’s assume that to be correct (as it is not relevant to your question anyway).
You have to remember that your salt only needs to be unique, not secret. It isn’t there as “a secret to secure data”. A salt is merely used to make things like passwords unique – slowing attackers down in situations where they might want to use things like hash-tables.
Second, if your application is stolen, having the salt logic on it gives the attacker an edge.
That argument isn’t really relevant in your scenario as the attacker would still have to “guess” the passwords – which are the real secrets (in contrast to the salts, better: the salting function within your application). That edge you see is rather minimal… and if you use a separate database server anyway, your attacker would be looking at a function within your application that can produce salts… but he/she would still have no copy of the database(s) to do anything useful with his/her ability to replicate the salts your application uses. For that, they would need to also steal the database.
Third, I use salt in the database and pepper in the application, this way the attacker needs to stole both the application and the database.
Great. Now all you need to do is to understand that a salt is not a secret. If an attacker knows the salt because – for example – he/she stole a copy of your database and can look it up quick and easy, that attacker would still need to brute-force the hashed passwords. Only when using a hash that is known to be cryptographically insecure, you would have given the attacker some kind of edge. But if you used something sane and cryptographically secure (like – for example – SHA256, HMAC256, or bcrypt), such an attacker will still be facing a loooong and painful brute-forcing attempt.
Knowing how to recreate salts will not magically make brute-forcing password hashes easy. That is, unless you use a cryptographically insecure password hashing method (like plain MD2 or MD5). Therefore, I tend to recommend that – instead of trying to secure non-secrets like salts – you rather focus on how you hash the real secrets… the passwords. See, I mentioned SHA256 above as a cryptographically secure hashing function, but it’s not the most optimal way to hash secrets like passwords. In fact, things like SHA256, HMAC256, or SHA512 (which you mentioned in relation to creating salts) are not meant to be used as password hashing functions and should - as a logic consequence - not be used for that purpose (not saying you should avoid them when creating unique salts). That‘s why I also mentioned bcrypt – which is one of many dedicated password hashing functions out there and definitely more recommendable when it comes to hashing passwords. Other good password hashing functions besides bcrypt would be scrypt, PBKDF2, or Argon2. No salt and/or pepper on this planet will save you when you mess up the password hashing part, but even the worst salt won’t be able to cause much damage if you implement the rest of your password hashing correct.
Salts are not secrets. They do not have to remain secret; they only need to be unique.
Non-secrets like salts (and peppers) can be stored “in the open”; in your case: as plaintext, integer value, or binary blob in your database. Both your application as well as your database can contain the functionality for creating unique salts.
Instead of trying to “secure” salts, it’s more important to focus on how you hash the passwords. Use a dedicated password hashing function to do so.