MD5 hashes are not the best way to hash passwords, it became "broken" (reversed engineered) years ago.
And just hashing a password is not a desirable security level. Using salts, hashing and iterations is. Here's the way I usually create and store a user password:
MySQL table:
create table `users` (
`id` int unsigned not null auto_increment,
...all user properties...
`salt` varchar(20) not null,
`pword` blob not null,
primary key(`id`)
) default charset=utf8 collate utf8_unicode_ci;
Generating a new user and creating a password:
define('SYSTEM_SALT', 'rgcaeg43cg0#3tg_..+g3gg3g'); /* A random system wide salt */
define('ITERATIONS', 1000); /* Number of hashing iterations */
define('KEY_LENGTH', 256); /* Length of the generated hash */
...
$user = new User();
$salt = PBKDF2::generator(15); /* Salt length is 15 characters */
$pword = PBKDF2::generator(8); /* User password length is 8 characters */
$hashed_pword = PBKDF2::run($pword, SYSTEM_SALT . $salt, ITERATIONS, KEY_LENGTH, PBKDF2::SHA512);
$user->setPassword($hashed_pword);
$user->setSalt($salt);
/* Set the rest of the user properties */
And finally, where the magic happens, the PBKDF2 class implementation:
class PBKDF2 {
const SHA256 = 'sha256';
const SHA512 = 'sha512';
const TIGER = 'tiger192,4';
const RIPEMD = 'ripemd256';
const HAVAL = 'haval256,5';
public static function run($password, $salt, $iterations, $key_length, $algorithm) {
if (!PBKDF2::isAlgorithmAvailable($algorithm))
throw new PBKDF2Exception("Algorithm not available on your system");
$key_blocks = ceil($key_length / strlen(hash($algorithm, null, true)));
$derived_key = '';
for ($block = 1; $block <= $key_blocks; $block++) {
$current = $initial = hash_hmac($algorithm, $salt . pack('N', $block), $password, true);
for ($i = 1; $i < $iterations; $i++) {
$current ^= ($initial = hash_hmac($algorithm, $initial, $password, true));
}
$derived_key .= $current;
}
return substr($derived_key, 0, $key_length);
}
public static function isAlgorithmAvailable($algorithm) {
return in_array($algorithm, hash_algos());
}
public static function generator($length = 8) {
$chars = 'abcdefghijklmnopqrstuvwxyz0123456789?!_:;,.ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789?!_:;,.';
$buffer = '';
for ($i = 0; $i < intval($length); $i++) {
$buffer .= $chars[mt_rand(0, strlen($chars) - 1)];
}
return $buffer;
}
}
PBKDF means Password Based Key Derivation Function
, and is a part of the PKCS standards. The class above hashes the password 1000 times (you could of course go higher), with the SHA 512 hash implementation in PHP (or your desierable algorithm) and two salts that were merged. This way you should be very secure against rainbow table lookups, which is the standard way of cracking a hashed password.
As you can see I also generate a password for the users, this could of course be non-autogenerated and instead let the users pick their own passwords.