views:

165

answers:

8

Hey everyone,

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,

+4  A: 
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 ) ;
}
Dan Heberden
How is the salt randomized? How is that salt used when authenticating the password upon login? Looking for a bit more depth here. Thanks!
barfoon
@barfoon are you **really** want your salt to be randomized?
Col. Shrapnel
@Col.Shrapnel: Indeed, per-entity - e.g. have a different salt for each user's password. (and using bcrypt instead of sha1 would not hurt either)
Piskvor
Using the salt on the db end is great for protecting your DB, but does nothing to prevent theft of a password while in transit. If a user has your plaintext password, they can masquerade as you.
thetaiko
More depth? I appended my answer. There is principle here, not just depth.
Dan Heberden
@Piskvor: According to [this answer](http://stackoverflow.com/questions/401656/secure-hash-and-salt-for-php-passwords/401684#401684) bcrypt is not safe enough: “Blowfish is a cipher, and to get repeatable results from a cipher you must have a password–if there is a password it can be reversed. *Blowfish was never designed to be destructive to data.* ”
Marcel Korpel
You always want to salt each password with a unique value. Otherwise someone could build one rainbow table, and use it on the entire DB. A salt is used in the first place to make it so that they would need to generate a new rainbow table for each and every password (MUCH more effort/cpu time)...
ircmaxell
To encode with the username is not a good idea, because what if the user can change his username? than the salt will not be the same, better is to use maybe return sha1( md5( $password ) . sha1( $password ) . base64_encode( $password ) ); or substr() and than the first 2 letters or sth like this.
ahmet2106
@ahmet2106 - i addressed that in my answer. "you'd have to make sure to update password if they ever changed the username" - that's not a big deal to implement that hook if you make a 'change username' feature.
Dan Heberden
@ircmaxell - but how does the user attemping to get in know the pattern? If my `$salt is sha1( substr($username, 2));` thus my resulting password is `sha1( $salt . $username );` that's a pattern, not a static salt. You could further alter the salt based on username to get a more complex effect.
Dan Heberden
@Dan Heberden: In practicality, your method is fine... I was actually talking against having a unique salt for the entire site (one)... However, that doesn't mean that in general what you're providing is a good idea (imagine a site with 10 million users, or simply if you get someone pissed off at you). If we're talking about the difference academically (as we are here), I think it's worth pointing out that a stronger salt will make things safer in general.
ircmaxell
@Marcel Korpel: That, unfortunately, is a bit of a misunderstanding - maybe I should've said "Eksblowfish" and avoided the confusion. You see, there's bcrypt the encryption (reversible), and there's bcrypt the password storage and verification scheme (designed to be nonreversible AND to slow down a cracking attempt).
Piskvor
+2  A: 

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.

thetaiko
Ahh ok makes sense. So in your DB ONLY the hashed password is stored?
barfoon
@barfoon - thats correct. Just a hash stored in the DB. Salt generated per user session. The benefit is that no plaintext password is ever passed across an unencrypted connection.
thetaiko
So your salt is always different per session. How do you know what the original salt was when the password was originally written to the database?
Marcel Korpel
@Marcel - This may be a lapse on my part, but I don't generally salt the hash of the password that I store in the DB.
thetaiko
He's talking about creating a token to protect against things like CSRF (and validating user input). That's not a salting function for salted hashes...
ircmaxell
+5  A: 

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.

Trevor Tippins
A: 

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;
}
Nort
Note: You lose entropy (number of possible hashes) if you don't include the original data in each hash call. So to improve this example significantly, simply do: `$hash = sha1(strlen($password) . md5($password) . $password . $salt);`
ircmaxell
A: 

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

oedo
+1  A: 

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.

chiborg
+2  A: 

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.

ircmaxell
A: 

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 avalible in the documentation.

Matthew Whited
Note: If you don't supply a salt with a hashing function specifier, this is really not secure at all:`The standard DES-based crypt() returns the salt as the first two characters of the output. It also only uses the first eight characters of str, so longer strings that start with the same eight characters will generate the same result (when the same salt is used).`
ircmaxell
Very true, but it also allows you to change the hash just by changing the salt prefix. `$1$`
Matthew Whited