The unique purpose of the salt is to be unique: no two hashed passwords shall use the same salt value. This is meant to prevent cost sharing such as precomputed hash tables.
Uniqueness should be worldwide. For instance, suppose that you used a simple counter as a salt. Correspondingly, the value "1" will be used as salt for the first created account, presumably the one for the server administrator. This will be true for all installed instances of that server. This makes it worthwhile to precompute a big table (e.g. a rainbow table) of potential hashed passwords using "1" as salt, because that table could be used to attack the administrator password on all servers which use that software. The word to meditate here is "worthwhile".
Since server-wide uniqueness is not enough, a counter will not be sufficient; the user name is not a good salt either (user names are unique on a given server, but there are many 'Bob', 'Joe' and 'DarkLord42' out there). An UUID (GUID) is supposed to do the trick. You can also use a strong uniform pseudorandom generator (/dev/urandom
, CryptGenRandom()
, java.security.SecureRandom
... depending on the platform you use) and get enough random bytes from it so that risks of reusing a salt value are sufficiently small; 16 bytes are enough for that. Using randomness is the "robust way" because it yields uniqueness with a sufficiently high probability, without having to rely on world-wide conventions.