"Why does using a random salt which can be found in the database add security over using a constant salt which can be found in the scripts?"
Because the "constant salt" (which would really be better called "pepper") is the same for all the password hashes, and therefore does not satisfy the primary purpose of a salt — ensuring that, even if two users have the same password, their hashes will be different.
In particular, let's say you have 1000 users, and that your user database with the password hashes gets stolen by an attacker. Very likely, the attacker can also steal your scripts, and thus also the pepper included in them. (This may not always be the case — for example, SQL injection attacks might compromise the database without necessarily compromising the application code — but you should not assume that it can't happen.)
To crack the password hashes, the attacker can then just guess random passwords by brute force (or, for a first pass, run through a list of common passwords), hash each of them with your fixed pepper and look the resulting hash up in a database of the stolen hashes. Notably, they need to do this only once for each guessed password, just as if you hadn't used the pepper at all.
However, if you had instead used a unique salt, stored in the database, for each password hash (with or without the pepper as well), then hashing a guessed password with one of the salts and comparing the result with the stolen hash just tells the attacker whether that particular user was using that particular password. Thus, the time needed to crack all the passwords (or to crack just any one or any n of them) is increased by a factor of 1000 (the number of users in your database).
Or, to look at it another way, not using a unique salt for each password hash in a 1000-user database makes the attacker's job 1000 times easier. Even if you used, say, PBKDF2 with an iteration count of 1000 to slow down brute force attacks, well, guess what — not using unique salts just ate away any benefit you got from using PBKDF2. Oopsie.
Now for the other part of your question, the article you linked to suggests a rather complicated method for choosing the salt:
$salt = hash('sha256', uniqid(mt_rand(), true) . 'something random' .
strtolower($username));
Why so complicated, when the only property the salt really needs to satisfy is uniqueness?
Well, the salt really should not just be unique within your database, but globally unique, so that even an attacker with multiple stolen databases won't find cracking them any easier. In some attack scenarios, it's also desirable for the salts to be unpredictable, so that an attacker can't start precomputing a password table before they've stolen your database, hopefully giving you more time to respond if you detect the breach.
A relatively simple way to achieve both of these goals is to make the salts sufficiently long and random (where sufficiently long means, say, at least 100 bits of entropy). However, generating truly random salts can be kind of tricky, especially in PHP which, unfortunately, still lacks a standard portable method for obtaining cryptographically secure random numbers.
Thus, the example code you linked to appears to be going for a robust "belt and suspenders" approach: by concatenating several pieces of data of varying randomness and uniqueness and feeding them to a cryptographic hash function, we can be fairly sure that the output will be as unique and random as the sum of the inputs (up to limits imposed by the hash output length, anyway, which for a 256-bit hash like SHA-256 are basically infinite). In particular, as long as at least one of the inputs is unique (resp. random), we can be essentially sure that the output will also be so.
As a general principle, this is a perfectly good and commendable approach to security. One might quibble about the specific choice of inputs suggested in the article (and, in particular, about the lack of even any attempt to obtain true cryptographic randomness from sources like openssl_random_pseudo_bytes() or /dev/urandom
on Unix systems), but at least the general design is sound.
In particular, note that the suggested inputs to the hash function include the following:
- the username (although why it's lowercased, I have no idea), ensuring that all users on the system will have unique salts,
- a hopefully unique per-system identifier (the "something random"), which should ensure that the same user will have different salts on different systems,
- the current time (obtained via uniqid()), which ensures that a user will get a different salt every time they change their password, and
- various other bits of (pseudo)randomness obtained via mt_rand() and uniqid(), which may introduce some unpredictability (and, at least in some hardened versions of PHP, possibly even some real randomness), and at least won't do any harm.