Jump to content

How do you know if your password encryption / security is correctly implemented?


Recommended Posts

I don't know if my password encrytion has been done correctly / is actually secure. I don't have anything valuable at the moment that people would care to hack, but in the future I want to be absolutely certain I am doing it right.

 

This is my process, I am storing it as Varchar(255), did a cost test and 9 was my result

$hash = password_hash($passsword, PASSWORD_BCRYPT, array("cost"=>9));

I was told I don't need a salt since it is included in the password_hash function

 

Also I noticed most of the hashes if not all start like this, why is that?

$2y$09$

Thanks for any help

Edited by moose-en-a-gant

You shouldn't specify the hashing algorithm. password_hash() will use the best one available by default, and if that changes from bcrypt in the future you'll want to make sure you're using it.

The hashes are backwards-compatible so you don't have to worry about a hash not working in the future.

 

The $2y$ is how PHP recognizes that the bcrypt algorithm was used to generate the hash. Check the documentation on crypt to see what salts look like: you'll see that the bcrypt ("b" stands for blowfish) salt is formatted as

"$2a$", "$2x$" or "$2y$", a two digit cost parameter, "$", and 22 characters from the alphabet "./0-9A-Za-z".

The password "hash" itself is a valid salt, since it was constructed to be salt + raw password hash. To verify a password, password_verify() salts the password using the hash and makes sure the new hash matches the old one. It is as simple as

crypt($password, $password_hash) == $password_hash

You should specifiy the hash algorithm and cost (like you already did). Do not rely on the default settings.

 

The whole point of modern password hashing is that you tune the settings for your specific hardware and requirements. You seem to have done that already, and that's great. However, you should store the parameters in a configuration file and automatically update the hashes when the settings change. This way you can easily keep the parameters up-to-date:

<?php

// put the hash parameters into a configuration file so that you can easily change them
define('APP_PASSWORD_HASH_ALGORITHM', PASSWORD_BCRYPT);
define('APP_PASSWORD_HASH_COST', 9);



// hashing a password (e. g. during registration)
$password_hash = password_hash($password, APP_PASSWORD_HASH_ALGORITHM, array('cost' => APP_PASSWORD_HASH_COST));



// verifying a password; if the parameters are out-of-date, the hash is automatically updated 
if (password_verify($password, $stored_hash))
{
	# do the log-in procedure

	// need a hash update?
	if (password_needs_rehash($stored_hash, APP_PASSWORD_HASH_ALGORITHM, array('cost' => APP_PASSWORD_HASH_COST)))
	{
		$new_password_hash = password_hash($password, APP_PASSWORD_HASH_ALGORITHM, array('cost' => APP_PASSWORD_HASH_COST));
	
		# do the UPDATE query
	}
}
else
{
	# password incorrect
}

Note that bcrypt hashes are always 60 ASCII characters long, so a CHAR(60) is sufficient.

 

Also note that bcrypt can only process up to 56 bytes of input, so you need to validate the user-provided password and warn them if it's too long:

<?php

// put this into a configuration file
define('APP_MIN_PASSWORD_LENGTH', 6);
define('APP_MAX_PASSWORD_LENGTH', 56);	// bcrypt can only process up to 56 bytes of input



if (mb_strlen($password, '8bit') < APP_MIN_PASSWORD_LENGTH)
{
	# too short
}
elseif (mb_strlen($password, '8bit') > APP_MAX_PASSWORD_LENGTH)
{
	# too long
}
else
{
	# length OK
}

Without this check, the password may be silently truncated (if it exceeds 72 bytes), and that obviously should never happen.

 

You shouldn't specify the hashing algorithm.

 

Did I? What I posted above is what I use.

 

I use password verify, I use this:

if (password_verify($passsword, $hash_from_db)) {
                   $_SESSION["user"]=$userrname;
               function Redirect($url, $permanent = false)
{
{
    if (headers_sent() === false)
    {
        header('Location: ' . $url, true, ($permanent === true) ? 301 : 302);
    }

    exit();
}

Redirect(' userpanel ', false);
           }

So if I use that for verification, and this for hashing, am I safe?

$hash = password_hash($passsword, PASSWORD_BCRYPT, array("cost" => 9));

You should specifiy the hash algorithm and cost (like you already did). Do not rely on the default settings.

 

The whole point of modern password hashing is that you tune the settings for your specific hardware and requirements. You seem to have done that already, and that's great. However, you should store the parameters in a configuration file and automatically update the hashes when the settings change. This way you can easily keep the parameters up-to-date:

<?php

// put the hash parameters into a configuration file so that you can easily change them
define('APP_PASSWORD_HASH_ALGORITHM', PASSWORD_BCRYPT);
define('APP_PASSWORD_HASH_COST', 9);



// hashing a password (e. g. during registration)
$password_hash = password_hash($password, APP_PASSWORD_HASH_ALGORITHM, array('cost' => APP_PASSWORD_HASH_COST));



// verifying a password; if the parameters are out-of-date, the hash is automatically updated 
if (password_verify($password, $stored_hash))
{
	# do the log-in procedure

	// need a hash update?
	if (password_needs_rehash($stored_hash, APP_PASSWORD_HASH_ALGORITHM, array('cost' => APP_PASSWORD_HASH_COST)))
	{
		$new_password_hash = password_hash($password, APP_PASSWORD_HASH_ALGORITHM, array('cost' => APP_PASSWORD_HASH_COST));
	
		# do the UPDATE query
	}
}
else
{
	# password incorrect
}

Note that bcrypt hashes are always 60 ASCII characters long, so a CHAR(60) is sufficient.

 

Also note that bcrypt can only process up to 56 bytes of input, so you need to validate the user-provided password and warn them if it's too long:

<?php

// put this into a configuration file
define('APP_MIN_PASSWORD_LENGTH', 6);
define('APP_MAX_PASSWORD_LENGTH', 56);	// bcrypt can only process up to 56 bytes of input



if (mb_strlen($password, '8bit') < APP_MIN_PASSWORD_LENGTH)
{
	# too short
}
elseif (mb_strlen($password, '8bit') > APP_MAX_PASSWORD_LENGTH)
{
	# too long
}
else
{
	# length OK
}

Without this check, the password may be silently truncated (if it exceeds 72 bytes), and that obviously should never happen.

 

Oh man this is great thank you, I like that password length check, I was concerned about using varchar(255), I mean I had read about 2*128 or something so 256 I figured was the standard for storing when using SHA255 or something. I think that it is unused space then. Specifying a larger length isn't a waste is it? Is that allocating spare space that is "taken" by empty spaces?

 

When you say configuration file, do you mean a stand alone php file that I include at the top of a registration page / login page?

Edited by moose-en-a-gant

A VARCHAR(n) doesn't allocate space for the unused characters. It does allocate one or two extra bytes to store the length, but that's of course irrelevant.

 

However, the problem of your VARCHAR(255) is that you lose type safety: MySQL will accept any string up to 255 characters, so if there's a bug in your application, you may end up storing tons of invalid hashes and not realize it. This cannot happen with a proper CHAR(60), because in that case, MySQL will actually warn you or even reject the data (in strict mode) if the input string is too long.

 

So I strongly recommend that you always use the correct size. This is also important for us, the programmers, because an explicit length tells us immediately what the application expects.

 

 

 

When you say configuration file, do you mean a stand alone php file that I include at the top of a registration page / login page?

 

Yes, something like that. Of course it doesn't have to be a PHP file, it might as well be XML, JSON, INI or whatever format you prefer. If you already have a configuration mechanism, simply add the hash parameters.

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.