Flourish PHP Unframework
This is an archived copy of the forum for reference purposes

Password hashing and logging in

posted by pxl 10 years ago

I'm not all that bright when it comes to cryptography, so I hope you guys can give me a hand.

I'm looking at building a simple login - Single user, one Auth level.

I have an install script which creates an initial password for the login. This password is then saved into the DB.

Once the user has finished installing they can then login using that password. However, it seems that fCryptography::hashPassword hashes the password differently each time it's passed.

How do I compare the password already saved in the DB with one that the user inputs to allow the user to login?

Thank you very much for your assistance!

Best,

PXL.

If you check out fCryptography#HashingandChecking, you'll see there is a method called fCryptography::checkPasswordHash() that can check a password against a hash.

The reason the hash is different each time is because a unique salt is used to help prevent rainbow table attacks. You can read some more about it at fCryptography#PasswordHashing.

posted by wbond 10 years ago

Will,

I simply can't understand how this makes any sense. If generating a hash uses a new salt each time, how can you validate a hash without also knowing the salt? If the salt changes every time, that would seem to imply you must store the salt to later validate the password. If you are storing the salt and the password in the db, you have no security.

Obviously, I am wrong here. However, the documents provide no explanation of how this can possibly work.

Here is some sample code to generate a hash per the documents.

<?php
/**
 * Get the base constants and includes
 */
require_once( realpath(dirname(__FILE__) . '/../includes/constants.php') );
require_once( realpath(dirname(__FILE__) . '/../includes/common_functions.php') );


echo "\\n";

$hash = fCryptography::hashPassword('Example password');
print_r($hash);

echo "\\n\\n";
?>

The result of this code in this single instance is : fCryptography::password_hash#VTcChsYK0f#61d3f97424db52a40e223c0878385a90ad82e979

Now, to validate a user password, I would need to use some code similar to this :

<?php
/**
 * Get the base constants and includes
 */
require_once( realpath(dirname(__FILE__) . '/../includes/constants.php') );
require_once( realpath(dirname(__FILE__) . '/../includes/common_functions.php') );


$password = 'Example password';

$checked = fCryptography::checkPasswordHash($password,'61d3f97424db52a40e223c0878385a90ad82e979');

if($checked === true)
{
    echo "\\nThis is the right password!\\n";
    
} else
    {
        echo "\\nThis is the NOT right password!\\n";
    }
?>

Needless to say, this does NOT work.

Without providing the salt, how in the world can checkPasswordHash produce the same hash used in the first example?

Again, I know I am doing something wrong here.

Please clarify.

posted by justbn 8 years ago

I THINK I have figured this out. Rather than storing just a 32bit hash as the password, you actually need to store an 80bit string such as "fCryptography::password_hash#B8CnJMDK29#acdec016d3e6608703f1684139dcfa40e424eb55" to validate a provided password against.

So, the validation code in the example above SHOULD have been:

<?php
/**
 * Get the base constants and includes
 */
require_once( realpath(dirname(__FILE__) . '/../includes/constants.php') );

$password = 'Example password';

$checked = fCryptography::checkPasswordHash($password,'fCryptography::password_hash#VTcChsYK0f#61d3f97424db52a40e223c0878385a90ad82e979');

if($checked === true)
{
    echo "\\nThis is the right password!\\n";
    
} else
    {
        echo "\\nThis is the NOT right password!\\n";
    }

?>

To save a bit of db space, you could actually just store the salt#hash combo such as "VTcChsYK0f#61d3f97424db52a40e223c0878385a90ad82e979" and use string concatenation to put the whole string together.


$sp = 'VTcChsYK0f#61d3f97424db52a40e223c0878385a90ad82e979';
fCryptography::checkPasswordHash($password,'fCryptography::password_hash#' . $sp);

Okay, so this explains the problem I was having. However, can someone explain the security of this? Isn't storing the hash and the password together insecure?

posted by justbn 8 years ago

Right, so in your first example you were breaking fCryptography because you were removing the fingerprint and the salt from the hash. fCryptography fingerprints hash for forwards compatibility. In the future I can add support for bcrypt and change the fingerprint and fCryptography will be able to accurately check the hashes.

In the second example you at least left the salt alone, but again you removed the fingerprint, which could cause your code to break with a future upgrade.

On to salting passwords. So when an attacker gets ahold of the contents of you database, they now have everyone's email address and hashed password. Since you did some research you know you should at least be hashing the passwords so the attacker doesn't get a nice easy list of email accounts and passwords. However, most users use passwords that are too short.

Rainbow tables are lists of precomputed hashes for short passwords. You can download a compressed MySQL database that includes the md5() of every 6 character or less password. Now they can take the hash stored in your database and look up all of the passwords, again with little effort.

What a salt does is lengthen short passwords to make rainbow table useless. Instead of simply look up a hash, they have to brute-force each password. This is the whole purpose of the salt. By its nature, since it is combined with the password to create the hash, it must be used on subsequent requests to verify the password. Because of this, it needs to be stored with the password. It is also imperative that each password have a different salt. If each password used the same salt, an attacker could build a rainbow table for the salt you used, and again do very simple lookups to get the passwords.

Complicating the fact is that many cryptographic hashes are intended to be fast, so that the performance of checking information is not too computationally expensive. Unfortunately this is a bad thing for password hashes. You want them to be as slow as possible, but not to slow that it affects the user when performing a login. There are specialized software packages out there that can generate millions to billions of md5() and sha1() hashes per second on GPU hardware. The simplest way to make this more expensive is to use many rounds of hashing. For instance, fCryptography uses sha1() since it is available on all PHP 5.1+ installs, but performs it 1000 times to make the hashing process slower. Inevitably this is not going to be enough and fCryptography will need to be "upgraded" to help prevent this.

The best way to hash a password is to use bcrypt. This algorithm uses a variable setup time, which means to can cause the algorithm to take as long as you want, helping making brute force attacks infeasible. Unfortunately this algorithm was not available on many platforms until PHP 5.3, and in the interest of compatibility, I chose to go with something I knew would work everywhere.

In the future I am planning on updating fCryptography to use bcrypt when available for hashing. This is where that fingerprint comes in nice and handy. :-)

posted by wbond 8 years ago

The PHPass project (http://www.openwall.com/phpass/) is an interesting place to look for PHP based password hashing. It uses salts, allows variable length stretching (the number of hashing rounds used) and is pretty portable. I've used it a few times and it is seeing some adoption amongst various CMSs. The code is public domain.

posted by anonymous 8 years ago