Creating and Verifying Hashes in PHP 5.5(jcurcio.com)
jcurcio.com
Creating and Verifying Hashes in PHP 5.5
http://www.jcurcio.com/posts/creating-and-verifying-hashes-in-php-the-easy-way/
43 comments
> There do exist experienced PHP devs who would do password storage the right way but PHP up until now neither made it easy nor encourage these practices as they relate to passwords.
As much as I welcome these features, I don't think that's true. PHP has made it extremely easy to use the core operating system's hashing features.
http://us3.php.net/manual/en/function.crypt.php
And after 5.3, even if for whatever reason your operating system is lacking, they were built in for convenience.
As much as I welcome these features, I don't think that's true. PHP has made it extremely easy to use the core operating system's hashing features.
http://us3.php.net/manual/en/function.crypt.php
And after 5.3, even if for whatever reason your operating system is lacking, they were built in for convenience.
crypt() is not extremely easy to use. Actually, it's fairly hard and easy to get wrong. The two things that people usually get wrong is a) salt generation and b) fixed-time comparison.
Except generating a random salt and the proper hash format to provide as input to crypt() is surprisingly difficult, especially in a cross-platform way. I recently replaced my custom crypt wrapper with the userland implementation of these functions, and in the process I discovered my custom wrapper had some rather non-obvious bugs.
I remember using BCrypt in PHP a few years ago. It was the first time I'd ever used BCrypt but it wasn't easy to find any resources on using it with PHP. Well at least not in the correct way.
>There do exist experienced PHP devs who would do password storage the right way but PHP up until now neither made it easy nor encourage these practices as they relate to passwords.
I'm happy to be one of them! The app I'm currently building uses this bcrypt library, and I can only highly recommend it:
http://www.openwall.com/phpass/
I'm happy to be one of them! The app I'm currently building uses this bcrypt library, and I can only highly recommend it:
http://www.openwall.com/phpass/
phpass is actually one of the libraries I was talking about. When I first encountered it I was really intimidated. The usage didn't seem that bad to figure out but trying to comb through it's page to find installation instructions was tough. It didn't seem clear about whether this was a module like imagemagick that needed to be installed or if it was a drop-in library you could just use within any project.
Looking back now it seems obvious but back then it just too overwhelming. With PHP there's this 'just get it done' attitude and futzing around with phpass wasn't something I could let myself do back then.
Of course I now regret it. I ended up using a different encryption library for password storage on my app. It isn't md5 but I know it could and should be stronger. So now I'm in a situation where I'm rewriting the codebase in a different language entirely and the cherry on top is that I need to migrate all my user's passwords and even some data from the original method to bcrypt, which I'm now using. It seems simple in theory but I have a feeling this is going to cause some headaches. However, its totally necessary and especially for the type of application I'm working on which puts a big focus on privacy and security.
Looking back now it seems obvious but back then it just too overwhelming. With PHP there's this 'just get it done' attitude and futzing around with phpass wasn't something I could let myself do back then.
Of course I now regret it. I ended up using a different encryption library for password storage on my app. It isn't md5 but I know it could and should be stronger. So now I'm in a situation where I'm rewriting the codebase in a different language entirely and the cherry on top is that I need to migrate all my user's passwords and even some data from the original method to bcrypt, which I'm now using. It seems simple in theory but I have a feeling this is going to cause some headaches. However, its totally necessary and especially for the type of application I'm working on which puts a big focus on privacy and security.
I am on md5 on one of my projects but it's salted alright. I want to change it. I reckon there is no way to migrate other than resetting every user password.
Actually, you can migrate slowly.
Create a second password column on your database to store your new hash. When a user logs in check if the new column is blank. If it is check that they entered the password correctly by verifying against your MD5 hash. If it is correct rehash their plain text and store it in the new column.
If the new column isn't blank, then the have already logged in and you have the new hash, so verify against the new hash. As everyone logs into your website, you'll have a new set of, more secure, hashes. For those that are inactive you can either delete them, assign a random password and email them, or let their MD5 password hash sit there forever.
If the new column isn't blank, then the have already logged in and you have the new hash, so verify against the new hash. As everyone logs into your website, you'll have a new set of, more secure, hashes. For those that are inactive you can either delete them, assign a random password and email them, or let their MD5 password hash sit there forever.
Don't forget to delete the value in the md5 column, otherwise the whole exercise is for naught.
Correct.
You could add a second password column to your users. Then if that column is empty, authenticate against the old password. If it passes, hash the password (just supplied by the user) with bcrypt and store it in the new column.
Over time, active users get their password upgraded.
Then after a longer time, just reset the passwords of the users who never logged in since you started migrating.
Over time, active users get their password upgraded.
Then after a longer time, just reset the passwords of the users who never logged in since you started migrating.
You can even do simpler.
On signup store:
Bonus the migration is instantaneous, you don't need a 6 month transition period.
IMHO it do not reduce the security, but i'm not a crypto expert though.
On signup store:
$password = password_hash(md5($password),PASSWORD_BCRYPT);
And on login: password_verify(md5($password), $password_hash);
Then you just have apply password_hash() on all you passwords in database.Bonus the migration is instantaneous, you don't need a 6 month transition period.
IMHO it do not reduce the security, but i'm not a crypto expert though.
It does add extra computational power to your log in though. While it is a minimal amount, depending on the size of your application it could be notable.
There is no way that it become notable. bcrypt is precisely designed to be slow and greedy in computational power.
So adding an extra md5 hashing is totally insignificant (in term of computation).
So adding an extra md5 hashing is totally insignificant (in term of computation).
> 'salt' => password_hash("MySalt",PASSWORD_BCRYPT)]
This is really, really dumb and pointless. In fact it makes absolutely no sense, it tells password_hash to use a bcrypted "MySalt" as a salt.
Not only is there no reason to explicitly provide a salt unless you already have bcrypted passwords in a non-standard format, (in which case you'd pass the existing $salt directly, you wouldn't bcrypt it) this is an inane way to generate one.
If you want to generate your salt by hand, don't do it. If you really, really, really want to, use mcrypt_create_iv.
This is really, really dumb and pointless. In fact it makes absolutely no sense, it tells password_hash to use a bcrypted "MySalt" as a salt.
Not only is there no reason to explicitly provide a salt unless you already have bcrypted passwords in a non-standard format, (in which case you'd pass the existing $salt directly, you wouldn't bcrypt it) this is an inane way to generate one.
If you want to generate your salt by hand, don't do it. If you really, really, really want to, use mcrypt_create_iv.
Adding that salt is optional. I'm pretty sure the author just added it to the post so people know it exists.
I don't think it's dumb and pointless at all, really. Generally you wouldn't generate a salt yourself if you're using bcrypt however I'm sure there would be cases where it would be needed or even preferred for some reason. I would advocate for flexibility and education rather than rigidity and simplicity. That is to say, rather than make the function useless to those who'd need their own salt I think it's a better idea to allow these options while making sure developers know that they should only be used in certain situations. A function like this isn't bad because it can be misused.
I don't think it's dumb and pointless at all, really. Generally you wouldn't generate a salt yourself if you're using bcrypt however I'm sure there would be cases where it would be needed or even preferred for some reason. I would advocate for flexibility and education rather than rigidity and simplicity. That is to say, rather than make the function useless to those who'd need their own salt I think it's a better idea to allow these options while making sure developers know that they should only be used in certain situations. A function like this isn't bad because it can be misused.
> Adding that salt is optional. I'm pretty sure the author just added it to the post so people know it exists.
I have nothing against adding a salt manually to show how it works, I have things against adding stupid salts.
> I don't think it's dumb and pointless at all, really.
You're missing the criticism. The problem isn't the ability to inject a salt (it is useful e.g. when the (cost, salt, hash) triple is in a format other than MCF), it is the way the salt is obtained.
I have nothing against adding a salt manually to show how it works, I have things against adding stupid salts.
> I don't think it's dumb and pointless at all, really.
You're missing the criticism. The problem isn't the ability to inject a salt (it is useful e.g. when the (cost, salt, hash) triple is in a format other than MCF), it is the way the salt is obtained.
Noted, and edited. Thanks for the suggestion.
You should have removed the salt entirely, after all the article is supposed to be "The easy way".
Edit: also the default cost is already 10, you should offer the $options as an aside and not suggest using them as standard practice.
Edit: also the default cost is already 10, you should offer the $options as an aside and not suggest using them as standard practice.
Why don't you generate your salt by hand?
Because there's very little point in doing so since bcrypt will automatically generate one for you.
A use case for manually injecting the salt is when your KDF result isn't stored in Modular Crypt Format, and thus you can't just pass it to `password_verify`.
In that case, you get your stored cost and salt, explicitly inject them into `password_hash` and use a constant-time string comparison (which I'm not sure PHP provides, so there's a potential security hole here) instead of `password_verify`.
A use case for manually injecting the salt is when your KDF result isn't stored in Modular Crypt Format, and thus you can't just pass it to `password_verify`.
In that case, you get your stored cost and salt, explicitly inject them into `password_hash` and use a constant-time string comparison (which I'm not sure PHP provides, so there's a potential security hole here) instead of `password_verify`.
There's also a library for PHP >= 5.3.7 that provides the same API if PHP 5.5 isn't an option yet.
https://github.com/ircmaxell/password_compat
Written by Anthony Ferrara, the same guy behind the `password_*` API in PHP 5.5
https://github.com/ircmaxell/password_compat
Written by Anthony Ferrara, the same guy behind the `password_*` API in PHP 5.5
There's also PHPass from OpenWall:
http://www.openwall.com/phpass/
http://www.openwall.com/phpass/
If you use PHPass, remember to check your hashes to make sure they're actually hashed with bcrypt. PHPass falls back to a less secure algorithm if bcrypt is not available in your PHP version. Most of the CMS's that claim to use PHPass actually use the "portable" option, which is based on MD5, because they want to remain compatible with PHP versions lower than 5.3 (the first version that is guaranteed to support bcrypt).
> Most of the CMS's that claim to use PHPass actually use the "portable" option
This is true for WordPress, which uses PHPass. You have to replace `wp_hash_password` to get WP to use bcrypt: http://wptip.me/wordpress-bcrypt
This is true for WordPress, which uses PHPass. You have to replace `wp_hash_password` to get WP to use bcrypt: http://wptip.me/wordpress-bcrypt
If you are using a PHP < 5.5, use this library:
https://github.com/ircmaxell/password_compat
If the password hashing APIs asks developers to generate a salt on their own it's neither easy nor safe.
I've worked on password hashing recently, and I think the best interfaces should take only a password, and return a hash. Modern password hashing algorithms, e.g., bcrypt, scrypt, etc., usually also need a set of local parameters. The API should hide them as well, and expose only a set of interfaces with safe and sound preconfigured parameters, which in turn determine how much CPU time and memory space will be used. One disadvantage of this approach is that it exposes the local parameters, hence make it a little bit easier for attackers.
This is exactly how crypto primitives have been designed. If the designers of AES ever let people choose the key size, sooner or later some people would shoot themselves in the foot, and set the key size to 1-bit. If you think this is an extreme example, it actually has happened, not in some obscure API that nobody uses, but in the very XMLDsig standard published by W3C [1].
Regarding password hashing standards, I found many misuses of libscrypt that would make it extremely easy to recover passwords. But I'll save the details for another blog post or something.
[1] http://www.w3.org/QA/2009/07/hmac_truncation_in_xml_signatu....
I've worked on password hashing recently, and I think the best interfaces should take only a password, and return a hash. Modern password hashing algorithms, e.g., bcrypt, scrypt, etc., usually also need a set of local parameters. The API should hide them as well, and expose only a set of interfaces with safe and sound preconfigured parameters, which in turn determine how much CPU time and memory space will be used. One disadvantage of this approach is that it exposes the local parameters, hence make it a little bit easier for attackers.
This is exactly how crypto primitives have been designed. If the designers of AES ever let people choose the key size, sooner or later some people would shoot themselves in the foot, and set the key size to 1-bit. If you think this is an extreme example, it actually has happened, not in some obscure API that nobody uses, but in the very XMLDsig standard published by W3C [1].
Regarding password hashing standards, I found many misuses of libscrypt that would make it extremely easy to recover passwords. But I'll save the details for another blog post or something.
[1] http://www.w3.org/QA/2009/07/hmac_truncation_in_xml_signatu....
This is an awesome improvement. I agree with BPatrianakos that hashing has not been easy enough for the average PHP dev - we hear that a lot.
As another option, you can also just not build any password infrastructure. We do all the hashing and authorization as a secure service and there are PHP devs of all levels using it... http://www.stormpath.com/docs/php/quickstart
As another option, you can also just not build any password infrastructure. We do all the hashing and authorization as a secure service and there are PHP devs of all levels using it... http://www.stormpath.com/docs/php/quickstart
Just noting if you're new to this you don't have to add a salt, by default password_hash with bcrypt will add a random one to each password.
Assuming the RNG isn't duff, which PHP has stumbled on a few times in the past, with session keys and such.
Check it out for yourself. Here's the random generating parts, pulled out for you: http://stackoverflow.com/questions/14673005/how-does-phps-pa...
How does that work? Don't you need the original salt to verify the hash?
The salt is stored in the hash itself, so it can extract the salt from any provided hash. (Neat huh?)
Here's a quick explanation of the format, in case anybody's interested:
http://pythonhosted.org/passlib/lib/passlib.hash.bcrypt.html...
http://pythonhosted.org/passlib/lib/passlib.hash.bcrypt.html...
[deleted]
Can someone explain to me what the "cost" option is?
Increasing cost by 1 doubles the time needed to create a hash.
A simple benchmark with results: http://pastebin.com/NiqKEAVm
A simple benchmark with results: http://pastebin.com/NiqKEAVm
One reason why Bcrypt has taken over for other hashing algorithms is because it introduces a "work" factor which allows you to extend the amount of time it takes to calculate the hash in order to prevent brute force attacks. By making the work factor or "cost" adjustable you get some protection against Moore's law because as computers get faster you can up the "cost".
Canonical article on why bcrypt which explains it here
http://codahale.com/how-to-safely-store-a-password/
Canonical article on why bcrypt which explains it here
http://codahale.com/how-to-safely-store-a-password/
The cost option is how algorithmically complex the resulting hash will be. The greater the cost, the longer it takes to create the final hash, but the more secure it is. Lower cost = lower secure.
Basically you want to use as high of a cost as you can on your servers hardware, without producing a noticeable slowdown to the end user.
Basically you want to use as high of a cost as you can on your servers hardware, without producing a noticeable slowdown to the end user.
It certainly makes handling passwords easier. Another option, though, would be to use Persona, in which case you don't store passwords at all.
Thanks for easily explaining this for everyone.
[deleted]
But that didn't always happen. There do exist experienced PHP devs who would do password storage the right way but PHP up until now neither made it easy nor encourage these practices as they relate to passwords. Now I hope word gets out about this and people stop using md5() thinking their passwords are safe. Not that md5 is necessarily bad, but most people don't realize there are better tools for maintaining secure passwords.
Also, this reminds me a little bit of `has_secure_password` method in Rails minus some of the automation that comes with it.