Is this strong enough? Am I missing some big issue?
Modern PHP actually provides exceptionally good password hashing built in - BCrypt, one of the three (SCrypt, BCrypt, PBKDF2) consistently recommended password hashing functions (as of early 2014). Even better, it handles salting for you (salt should simply be random and long enough).
If you're on PHP 5.5 or later, please read Safe Password Hashing at PHP.net - this is the FAQ for storing passwords in PHP. If you are on PHP 5.3.7 but not yet 5.5, you can use the password_compat library to use these functions.
These functions use BCrypt and handle the salt for you, so it's easy!
In particular, you can hash the password with a high enough cost (pick a cost that takes just long enough that under your expected maximum load, your site will be not quite CPU bound) - like in password_hash Example 2:
<?php
/**
* In this case, we want to increase the default cost for BCRYPT to 12.
* Note that we also switched to BCRYPT, which will always be 60 characters.
*/
$options = [
'cost' => 12,
];
echo password_hash("rasmuslerdorf", PASSWORD_BCRYPT, $options)."\n";
?>
Then you store the string it returns.
To verify, retrieve the string it returned from wherever you stored it (i.e. your database) and compare with the password_verify example:
<?php
// See the password_hash() example to see where this came from.
$hash = '$2y$07$BCryptRequires22Chrcte/VlQH0piJtjXl.0t1XkA8pw9dMXTpOq';
if (password_verify('rasmuslerdorf', $hash)) {
echo 'Password is valid!';
} else {
echo 'Invalid password.';
}
?>
As always, if you want details, please read How to securely hash passwords? - but the PHP 5.5 password functions use Bcrypt, so you can just use a high enough cost.
Note that as time goes on and you buy better hardware, you should increase the cost to match. You can transparently do that by, after verifying the password at the old cost, checking for the old cost and if found, rehashing it with a new cost as part of the login process, so you can increase the security of the stored passwords without bothering your users. Of course, users who never log in don't get the benefits of this.
For the old pre-5.3.7 crypt() example, see the leading answer to How do you use bcrypt for hashing passwords in PHP?, which has a getSalt function of:
private function getSalt() {
$salt = sprintf('$2a$%02d$', $this->rounds);
$bytes = $this->getRandomBytes(16);
$salt .= $this->encodeBytes($bytes);
return $salt;
}