Jump to content

Recommended Posts

Possible? probably, and I mean that in the "anything's possible" kind of way.

Likely to happen in the real world? No, unless for some crazy crazy reason you use a static salt for all passwords)

How to prevent it? Why would you bother? Even if it were to happen, what harm do you envisage? Technically you could put a unique index on the DB column and then request the user try a different password if it throws a duplicate entry error (just don't tell the user why their password doesn't work!). But the chances of the same random salt being generated for the same password the one time another user registers it is infinitesimal (like one times ten to the minus several billion). Of course enforcing a good password requirement will reduce the chance of raw password collisions as well.

If so, how to prevent this situation?

Don't do anything. The only thing you should ever do with a password is make sure it is "secure enough": letters, numbers, symbols, minimum length, not a common password, doesn't contain their username, that stuff. The odds of two users picking the same password and getting the same salt are astronomical and, most importantly, it doesn't matter if it happens. (Would be cool to see, though.)

 

in b4 jacques

  • Like 1

@requinix I'd like to see the numbers on how many users it would take to guarantee a duplicate hash assuming every one used the same password. Also I like using phrases for passwords over actual words eg. "I used to live at 221b Bakers Street, but got fed up with Danger Mouse ruining all the letters I put in the post box" would be a good one  :happy-04:

Not so much related with the above subject/comments, but might be interesting to know; I have been playing with an online php editor (http://sandbox.onlinephpfunctions.com/).

$string='hel$lo';
echo 'string is '.$string."\n";//displays "string is hel$lo"
echo password_hash($string, PASSWORD_DEFAULT)."\n";

is ok, while

$string="hel$lo";
echo 'string is '.$string."\n";//displays "string is hel"
echo password_hash($string, PASSWORD_DEFAULT)."\n";

would still generate a hash, but also an "Undefined variable: lo" warning, apparently due to double quotes. Actually only "hel" is taken into account.

double quotes create what I call "loose" stings, any PHP special chars - such as $ are parsed as code whilst within the string.  single quotes make absolute strings, so everything inside them is taken as a part of the string literal (with the exception of the escape character). 

 

see here for more detailed info on PHP strings.

@Jacques1 : thanks for the link, though I'd be lying if I said I understood half of the formula on that page.  It doesn't look like the answer to what I was asking though.  Assuming URANDOM is being used to create the salt, how many times would the same password have to be put trough the same algorithm in order to guarantee a collision?

 

Is that what you answered?

Is that what you answered?

 

A random number generator doesn't guarentee anything – except the trivial fact that you're guaranteed to get duplicates when you've run out of salts. If that's what you wanted to know, the answer is 2^128 + 1 (the number of possible 128-bit salts and one more).

 

A much more practical problem is to actually take the randomness into account and calculate the number of required users to reach a certain duplicate probability. That's what I was referring to in my reply: To get a 50% chance of identical hashes when the salts are random and the passwords are idential, you need around 2.2E19 users.

 

The latter number is a lot smaller and can actually be alarming for shorter salts. For example, a 64-bit salt seems astronomical as well when you look at the number of possible values (~10^19). But it only takes a few billion users to end up with a 50% chance of a collision. This may not happen in a single application, but it will certainly happen accross multiple applications.

  • Like 1

Yeah, the first bit was what I was asking about.  I wasn't sure what the word length of URANDOM was, so didn't know.  Still 2^128+1 doesn't really register to me, so I fond this page that helps conceptualise the size of that number:

 

http://bugcharmer.blogspot.co.uk/2012/06/how-big-is-2128.html

As to the original question: While the salt isn't a problem, there are problems with the code.

 

Do not rely on PASSWORD_DEFAULT. The whole point of modern password hash algorithms is to make the hashing as expensive as possible within the limits of your hardware. If you just go with the (low) default parameters, your hashes will be weaker than necessary. A better stratety is to choose a specific algorithm (PASSWORD_BCRYPT is the only one right now) and then increase the cost parameter until the duration is no longer acceptable. The previous value is the optimimum.

 

Also don't hash passwords directly. The current bcrypt algorithm in particular cannot handle arbitrary input. Long passphrases (like the one by Muddy_Funster) and null bytes will lead to truncation, meaning the hash is again a lot weaker than expected. A common workaround is to pre-hash the password with another hash algorithm like SHA-256:

<?php

function bcrypt_hash($password, $cost)
{
    /*
     * Pre-Hash the password with SHA-256 to obtain a 256-bit binary hash. Then encode the binary hash with Base64 to get a
     * string of 43 ASCII characters. This makes sure the string is within the length limit of bcrypt (56 bytes) and doesn't
     * contain any null bytes.
     */
    $binary_prehash = hash('sha256', $password, true);
    $encoded_prehash = rtrim(base64_encode($binary_prehash), '=');

    return password_hash($encoded_prehash, PASSWORD_BCRYPT, ['cost' => $cost]);
}



$raw_password = 'I used to live at 221b Bakers Street, but got fed up with Danger Mouse ruining all the letters I put in the post box';
$cost = 12; // adjust this to your current hardware

var_dump( bcrypt_hash($raw_password, $cost) );

---

 

 

Yeah, the first bit was what I was asking about.  I wasn't sure what the word length of URANDOM was, so didn't know.

 

128 is the bit length of bcrypt salts. It has nothing to do with randomness. You'd have the same limit if you used sequential salts.

Edited by Jacques1

Thanks Jacques.

 

What would be the right password_verify() syntax ?

 

I applied your function to "hello" as password, and obtained string(60) "$2y$12$4HJ4ttxHjrmhnNamYZPftehUIlZ.IkQndxDca50kid.lmSXLjhTqy".

 

Then I ran :

$hash = '$2y$12$4HJ4ttxHjrmhnNamYZPftehUIlZ.IkQndxDca50kid.lmSXLjhTqy';

if (password_verify('hello', $hash)) {
    echo 'Password is valid!';
} else {
    echo 'Invalid password.';
}

And got "Invalid password."

You have to go through the exact same prehashing procedure as above.

function bcrypt_verify($password, $hash)
{
    $binary_prehash = hash('sha256', $password, true);
    $encoded_prehash = rtrim(base64_encode($binary_prehash), '=');

    return password_verify($encoded_prehash, $hash);
}

As explained in the second answer, the mysql_* functions are ancient and have been removed from PHP. Manual SQL-escaping in general is deprecated, because it's far too error-prone (as you can see).

 

Nowadays, we use PDO (or mysqli) together with prepared statements. The query string is supposed to be entirely static with no variable insertion of any kind. If you need dynamic input, you use query parameters and then bind the input to those parameters.

 

This not only solves your current problem. It eliminates the risk of SQL injections altogether (as long as the query strings are in fact static).

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.