After reading about salts password hashing Id like to implement a simple version for an admin area to a site Im building.
If you have any good links with code that have implemented this idea well, I would appreciate it if you could share.
Thanks,
After reading about salts password hashing Id like to implement a simple version for an admin area to a site Im building.
If you have any good links with code that have implemented this idea well, I would appreciate it if you could share.
Thanks,
Registration process: User enters a password. System generates a salt value from random data (could be a hash of the time & PID or something). Systems generates a hash value of the password & salt value and stores both of these in the registration table.
Login process: User enters a password. System pulls the salt value from the database and hashes it and the password and compares that against the hashed password value put into the database during registration.
The plaintext password is never stored in the database. The salt value is never visible to the client.
Well, here's what I would do:
function makeToken($length = 16) {
if ($length > 16) {
$ret = '';
while ($length > 0) {
$ret .= makeToken(16);
$length -= 16;
}
if ($length < 0) {
$ret = substr($ret, 0, $length);
}
return $ret;
}
$stub = '';
for ($i = 0; $i < 100; $i++) {
$stub .= chr(mt_rand(1, 254));
}
$hash = sha1($stub);
$hashLen = strlen($hash);
$ret = '';
for ($i = 0; $i < $length; $i++) {
$ret .= $hash[mt_rand(0, $hashLen - 1)];
}
return $ret;
}
function makeSaltedHash($string, $salt = '') {
if (empty($salt)) {
$salt = makeToken();
}
$hash = '';
for ($i = 0; $i < 200; $i++) {
$hash = sha1($hash . $salt . $string);
}
return $hash . ':' . $salt;
}
function verifySaltedHash($string, $hash) {
if (strpos($string, ':') === false) return false;
list ($base, $salt) = explode(':', $hash);
$test = makeSaltedHash($string, $salt);
return $test === $hash;
}
The rational is this:
First, generate a random salt (this will always return a hex string, so it can be used for tokens etc). Then loop over a hashing function (sha1 in this case) more than 1 time. This is so that it becomes more expensive to generate a rainbow table (200x as expensive in this case) while adding relatively little expense to generation.
Usage:
To generate a hash:
$hash = makeSaltedHash($password);
To verify the hash:
$bool = verifySaltedHash($password, $savedHash);
To generate a token (CSRF protection, session_id, etc), you can do it a few ways:
Fixed Length:
$token = makeToken(32);
Random Length:
$token = makeToken(mt_rand(64,128));
Edit: Increased the repetitions on sha1 in the hashing function.
function encodePwd($salt, $string) {
return sha1( $salt . $string );
}
think about salt randomization for a minute though. Password encoding specifically.
If i have salt of "random" and a password of "complex", my sha1 would be
e55ec45f2873a04d2b888a5f59dd3f9d3bb25329
that's stored in the database. I want to check against that.
So when a user supplies me "complex" as a password, i tag "random" in front of it and encode it to get the same hash. If they equal, then bazinga! i'm set.
But what if that was random?
salt when it was stored: "random"
SHA1: e55ec45f2873a04d2b888a5f59dd3f9d3bb25329
salt when the user put it in: "apple"
SHA1: e07b207d77a0bd27d321552fc934b186559f9f42
how am i going to match those?
If you are looking for a more secure method, use data that you have and that is constant like the username or id of user or something (preferably something that won't change). You need a pattern you can rely on.
username would work good (you'd have to make sure to update password if they ever changed the username) that way authentication could look like
`WHERE `username` = '&username' AND `password` = '" . encodePwd( $username, $password ) . "'"`
function encodePwd( $username, $password) {
// maybe modify username on a non-random basis? - like
// $username = sha1( substr($username, 2)); // assuming usernames have a min-length requirement
return sha1( $username . $password ) ;
}
I don't have a link to available code, but what I've done in the past is to generate a randomized salt - $salt = rand(1,1000000000);
- and save it in a session. I pass that salt to a login page and then use JavaScript to create a SHA hash of the salt + password which is submitted rather than a plaintext password. Since the salt is stored in the session I can then use that to see if the login hash matches the salt + password hash stored in the db.
If you need really secure hashes, please use the Portable PHP hashing framework.
I'd also recommend this Month of PHP security article that deals extensively with password hashing and the security of hashes.
There are so many ways you can create a salt string, but i think you don't need to think a lot about your salt strength.
I hash passwords like this
$hash = sha1(strlen($password) . md5($password) . $salt);
I think its the best performance between speed, and "security".
function salt($lenght = 9) {
$numbers = '0123456789';
$chars = 'qwertzuiopasdfghjklyxcvbnm';
$password = '';
$alt = time() % 2;
for ($i = 0; $i < $length; $i++) {
if ($alt == 1)
{
$password .= $chars[(rand() % strlen($chars))];
$alt = 0;
} else
{
$password .= $numbers[(rand() % strlen($numbers))];
$alt = 1;
}
}
return $password;
}
the code is simple, and dan heberden has already provided it.
a salt is simply a piece of text that you append or prepend to a password before generating a hash. eg, if your password is 'password' and the salt is 'salt' then the hash will be hashFunction('saltpassword')
instead of hashFunction('password')
.
salts are generally used to avoid rainbow password cracks - this is where a large list of passwords and their hashes are checked against the hashed password. eg in the above example, say there is a hash 123456 which corresponds to hashFunction('password')
, if the attacker knows your hash is 123456 then they know your password is 'password'.
your salt should be a random string of letters and numbers - eg kjah!!sdf986. it's very very unlikely for someone to have a rainbow table including kjah!!sdf986password so even if someone gets your hashed password then it's kinda useless.
however, you obviously need to use the same salt every time, or at least store the salt as well as the password. because if you pick a random salt every time chances are your hashed salt+password will not be the same :D
Could you use the built in crypt(...)
function?
... Usage ...
crypt('rasmuslerdorf', '$1$rasmusle$')
... Result ...
MD5: $1$rasmusle$rISCgZzpwk3UhDidwXvin0
... There are more examples and other hash methods available in the documentation.