Hi everybody!
In issue #64816[1] the OP suggests in the comment from [2015-05-05 04:34
UTC] that hash_pbkdf2()
should be recommended for advanced users, and
that password_hash()
should use PBKDF2 with at least 128,000 rounds.
The "Adding simple password hashing API" RFC[2] mentions in the "Future
concerns" section that new hash algorithms may be introduced, and that
the default algorithm as well as the default cost may be changed.
According to the "Updating PASSWORD_DEFAULT" section[3] changing the
default algorithm for PHP 7.0 is not possible anymore, but it might be
considered to add support for PBKDF2, and to increase the cost of the
CRYPT_BLOWFISH
algorithm.
Thoughts?
[1] https://bugs.php.net/bug.php?id=64816
[2] https://wiki.php.net/rfc/password_hash#future_concerns
[3] https://wiki.php.net/rfc/password_hash#updating_password_default
--
Christoph M. Becker
While I agree that the statement "bcrypt is better than PBKDF2, thus only
bcrypt should be used" is difficult to defend, I think saying "bcrypt is a
homegrown solution, only PBKDF2 is a good way to do it" is also wrong and
OP is opinionated.
IMO - docs should describe alternatives, without statements "X is better
than Y", but we also should include PBKDF2 as an option for password_hash()
- PHP7 is (theoretically, apparently) closed for new features, but we
should target the next possible version. I'm not sure if we should opt for
changing the default.
Regarding iteration count: (again, IMO) 1024 is a bit low, but 128000 as a
default for everyone might be a bit too much.
--Leszek
Hi everybody!
In issue #64816[1] the OP suggests in the comment from [2015-05-05 04:34
UTC] thathash_pbkdf2()
should be recommended for advanced users, and
thatpassword_hash()
should use PBKDF2 with at least 128,000 rounds.The "Adding simple password hashing API" RFC[2] mentions in the "Future
concerns" section that new hash algorithms may be introduced, and that
the default algorithm as well as the default cost may be changed.
According to the "Updating PASSWORD_DEFAULT" section[3] changing the
default algorithm for PHP 7.0 is not possible anymore, but it might be
considered to add support for PBKDF2, and to increase the cost of the
CRYPT_BLOWFISH
algorithm.Thoughts?
[1] https://bugs.php.net/bug.php?id=64816
[2] https://wiki.php.net/rfc/password_hash#future_concerns
[3] https://wiki.php.net/rfc/password_hash#updating_password_default--
Christoph M. Becker
Leszek Krupinski:
While I agree that the statement "bcrypt is better than PBKDF2, thus only
bcrypt should be used" is difficult to defend,
Well at least the StackExchange thread[1] pointed out by Nikita supports
the statement.
I think saying "bcrypt is a
homegrown solution, only PBKDF2 is a good way to do it" is also wrong and
OP is opinionated.
ACK.
IMO - docs should describe alternatives, without statements "X is better
than Y", but we also should include PBKDF2 as an option forpassword_hash()
- PHP7 is (theoretically, apparently) closed for new features, but we
should target the next possible version. I'm not sure if we should opt for
changing the default.
As Nikata has pointed out there is no standardized crypt-compatible
format for PBKDF2, so it seems to be preferable not to add it to
password_hash()
.
Regarding iteration count: (again, IMO) 1024 is a bit low, but 128000 as a
default for everyone might be a bit too much.
As I understand it, the iteration count has to be very different for
bcrypt and PBKDF2 (the latter requiring much more rounds). Increasing
the default cost factor of bcrypt from 10 to 11 or 12 seems to be
reasonable, considering that 10 had be chosen nearly two years ago.
--
Christoph M. Becker
The iteration count is very different because in bcrypt it's not an
iteration count number at all, it's a "cost". And it's kinda exponential: a
hash with a cost of 11 is twice as hard to compute than that of a 10. At
our company we are using a cost of 11 right now, which means a hash is
computed in around 100ms in a Core i7
Leszek Krupinski:
While I agree that the statement "bcrypt is better than PBKDF2, thus only
bcrypt should be used" is difficult to defend,Well at least the StackExchange thread[1] pointed out by Nikita supports
the statement.I think saying "bcrypt is a
homegrown solution, only PBKDF2 is a good way to do it" is also wrong and
OP is opinionated.ACK.
IMO - docs should describe alternatives, without statements "X is better
than Y", but we also should include PBKDF2 as an option for
password_hash()
- PHP7 is (theoretically, apparently) closed for new features, but we
should target the next possible version. I'm not sure if we should opt
for
changing the default.As Nikata has pointed out there is no standardized crypt-compatible
format for PBKDF2, so it seems to be preferable not to add it to
password_hash()
.Regarding iteration count: (again, IMO) 1024 is a bit low, but 128000 as
a
default for everyone might be a bit too much.As I understand it, the iteration count has to be very different for
bcrypt and PBKDF2 (the latter requiring much more rounds). Increasing
the default cost factor of bcrypt from 10 to 11 or 12 seems to be
reasonable, considering that 10 had be chosen nearly two years ago.--
Christoph M. Becker
Albert Casademont wrote:
The iteration count is very different because in bcrypt it's not an
iteration count number at all, it's a "cost". And it's kinda exponential: a
hash with a cost of 11 is twice as hard to compute than that of a 10. At
our company we are using a cost of 11 right now, which means a hash is
computed in around 100ms in a Core i7
A cost of N means 2**N rounds (i.e. iteration counts). Therefore a cost
of 10 means 1024 rounds. However, the complexity of the underlying
primitive should affect what is to be considered a reasonable iteration
count. For instance, CRYPT_BLOWFISH
has a minimum of 16 rounds, while
CRYPT_SHA256 has a minimum of 1000.
--
Christoph M. Becker
Leszek Krupinski:
While I agree that the statement "bcrypt is better than PBKDF2, thus only
bcrypt should be used" is difficult to defend,Well at least the StackExchange thread[1] pointed out by Nikita supports
the statement.
Partially. It mainly says "bcrypt is no worse than PBKDF2". As Nikita
stated, bcrypt is better in putting more strain on GPU, yet it's worse with
long passwords. Because of that, I wouldn't say which one is generally
better.
IMO - docs should describe alternatives, without statements "X is better
than Y", but we also should include PBKDF2 as an option for
password_hash()
- PHP7 is (theoretically, apparently) closed for new features, but we
should target the next possible version. I'm not sure if we should opt
for
changing the default.As Nikata has pointed out there is no standardized crypt-compatible
format for PBKDF2, so it seems to be preferable not to add it to
password_hash()
.
It's true that it's not supported by crypt, but I'll refer to that in a
reply to Nikita's post.
Regarding iteration count: (again, IMO) 1024 is a bit low, but 128000 as
a
default for everyone might be a bit too much.As I understand it, the iteration count has to be very different for
bcrypt and PBKDF2 (the latter requiring much more rounds). Increasing
the default cost factor of bcrypt from 10 to 11 or 12 seems to be
reasonable, considering that 10 had be chosen nearly two years ago.
+1.
--Leszek
Hi everybody!
In issue #648161 the OP suggests in the comment from [2015-05-05 04:34
UTC] thathash_pbkdf2()
should be recommended for advanced users, and
thatpassword_hash()
should use PBKDF2 with at least 128,000 rounds.
PBKDF2 is known to be weaker than bcrypt for the password hashing usage, as
it is more amenable to implementation on GPUs etc. 1 You can find
estimated hardware costs for PBKDF2 / bcrypt / scrypt with different
settings in the script paper [2, p.14]. PBKDF2 is only stronger than bcrypt
for passwords longer than 72 characters. Note that the scrypt paper assumes
a maximum length of 55 characters, which means the results in the last
column are likely not representing bcrypt correctly.
The primary reason why PBKDF2 is preferable in some contexts (e.g.
government contracts) is that it is approved by NIST in SP 800-132 3,
although there is some disagreement as to whether this recommendation even
applies for use as a password hashing function, as opposed to usage as a
KDF.
It should be further noted that there is no standardized crypt()
format for
PBKDF2 and password_hash()
is a crypt-compatible API. As such supporting
PBKDF2 there would be very problematic. We do already support it in the
form of hash_pbkdf2()
and people who wish to use this method (e.g. due to
legal restrictions) can use it through this API - I don't see a reason to
have it in password_hash()
, which should only support the recommended
default use case.
http://csrc.nist.gov/publications/nistpubs/800-132/nist-sp800-132.pdf
Nikita
Nikita Popov wrote:
In issue #648161 the OP suggests in the comment from [2015-05-05 04:34
UTC] thathash_pbkdf2()
should be recommended for advanced users, and
thatpassword_hash()
should use PBKDF2 with at least 128,000 rounds.PBKDF2 is known to be weaker than bcrypt for the password hashing usage, as
it is more amenable to implementation on GPUs etc. 1 You can find
estimated hardware costs for PBKDF2 / bcrypt / scrypt with different
settings in the script paper [2, p.14]. PBKDF2 is only stronger than bcrypt
for passwords longer than 72 characters. Note that the scrypt paper assumes
a maximum length of 55 characters, which means the results in the last
column are likely not representing bcrypt correctly.The primary reason why PBKDF2 is preferable in some contexts (e.g.
government contracts) is that it is approved by NIST in SP 800-132 3,
although there is some disagreement as to whether this recommendation even
applies for use as a password hashing function, as opposed to usage as a
KDF.
Thanks, Nikita, for the detailed explanation and the links. Sounds very
convincing to me. :)
It should be further noted that there is no standardized
crypt()
format for
PBKDF2 andpassword_hash()
is a crypt-compatible API. As such supporting
PBKDF2 there would be very problematic. We do already support it in the
form ofhash_pbkdf2()
and people who wish to use this method (e.g. due to
legal restrictions) can use it through this API - I don't see a reason to
have it inpassword_hash()
, which should only support the recommended
default use case.
Thanks for the explanation. I agree that it doesn't make sense to add a
non-standard crypt format for PBKDF2.
--
Christoph M. Becker
It should be further noted that there is no standardized
crypt()
format for
PBKDF2 andpassword_hash()
is a crypt-compatible API. As such supporting
PBKDF2 there would be very problematic. We do already support it in the
form ofhash_pbkdf2()
and people who wish to use this method (e.g. due to
legal restrictions) can use it through this API - I don't see a reason to
have it inpassword_hash()
, which should only support the recommended
default use case.
http://csrc.nist.gov/publications/nistpubs/800-132/nist-sp800-132.pdf
That's true that there's no support for pbkdf2 in crypt. On the other hand,
the RFC for password_hash stated:
These hashing APIs will initially be thin wrappers around crypt() to
allow for automatic salt generation and better error checking.
It says "initially" - that's why I understood that in the future we can
diverge from crypt in situations when there's a great new hash we would
like to use, but it's not supported by crypt (yet or at all).
Also, I'm pro-choice ;) We have an extensible API for password_hash()
, and
because people have different needs (like gpu strain in bcrypt or longer
passwords in pbdkf2) we should provide an option for more experienced
users, while having reasonable defaults. Yes, advanced users can use hash
functions directly, but password_* are so nice :)
--Leszek
Leszek Krupinski wrote on 07/05/2015 07:11:
It should be further noted that there is no standardized
crypt()
format for
PBKDF2 andpassword_hash()
is a crypt-compatible API. As such supporting
PBKDF2 there would be very problematic.
That's true that there's no support for pbkdf2 in crypt. On the other hand,
the RFC for password_hash stated:These hashing APIs will initially be thin wrappers around crypt() to
allow for automatic salt generation and better error checking.It says "initially" - that's why I understood that in the future we can
diverge from crypt in situations when there's a great new hash we would
like to use, but it's not supported by crypt (yet or at all).
Notwithstanding the merits of using it in this case, I wonder if there's
a possibility to invent a syntax recognised by password_hash which is an
explicit extension of what crypt uses, and is guaranteed not to collide,
kind of like the "X-" header prefix in HTTP.
If a "standard" format for the same algorithm was added in future,
password_needs_rehash could return true for the deprecated interim
format, allowing a smooth transition. (If you explicitly chose PBKDF2,
you would still need to call that function to allow for increases in the
cost parameter.)
Regards,
Rowan Collins
[IMSoP]
Leszek,
It should be further noted that there is no standardized
crypt()
format for
PBKDF2 andpassword_hash()
is a crypt-compatible API. As such supporting
PBKDF2 there would be very problematic. We do already support it in the
form ofhash_pbkdf2()
and people who wish to use this method (e.g. due to
legal restrictions) can use it through this API - I don't see a reason to
have it inpassword_hash()
, which should only support the recommended
default use case.
http://csrc.nist.gov/publications/nistpubs/800-132/nist-sp800-132.pdfThat's true that there's no support for pbkdf2 in crypt. On the other hand,
the RFC for password_hash stated:These hashing APIs will initially be thin wrappers around crypt() to
allow for automatic salt generation and better error checking.It says "initially" - that's why I understood that in the future we can
diverge from crypt in situations when there's a great new hash we would
like to use, but it's not supported by crypt (yet or at all).
That was precisely the intention. For that exact reason.
HOWEVER (and this is a big however), I think we should avoid doing
that unless absolutely necessary. We should strive to build off
standardized crypto. This is good both from an interoperability
standpoint, and the standpoint of relying on cryptographers to set the
standard. We have few if any professional cryptographers on this
list/project, so we should avoid as much as possible inventing our own
(unless absolutely necessary, in which we can elicit the help of
professionals).
Also, I'm pro-choice ;) We have an extensible API for
password_hash()
, and
because people have different needs (like gpu strain in bcrypt or longer
passwords in pbdkf2) we should provide an option for more experienced
users, while having reasonable defaults. Yes, advanced users can use hash
functions directly, but password_* are so nice :)
Well, I'm not sure I agree here. I think the point of password_hash()
is to raise the bottom barrier. I have said multiple times that it is
not a replacement for crypt(3) or other more complicated tools. It's
meant more as a way for non-experts to implement secure code.
If you need to make a different tradeoff, then you can always use a
different library or more advanced use case. I think that
password_hash()
should remain simple, with as few choices as possible
(less for users to screw up).
As far as length protection, you can simply pre-hash with sha-512 and
base64_encode the result:
$prehash = base64_encode(hash('sha512', $password, true));
$hash = password_hash($prehash, PASSWORD_DEFAULT, []);
This is also "inventing" crypto, so I would be extremely wary of
recommending this as a project. If we can get the upstream providers
(specifically the OpenBSD project) to add a crypt(3) format for this
specific case, then we can integrate it into our crypt(3)
implementation, and hence password_hash()
. But I would want the
upstream to do it first.
The only algorithm at present that I have seen that today could
replace bcrypt in password_hash is scrypt. However, there are 2
fundamental problems with it: 1) there's no crypt(3) binding or
format. 2) At similar cost settings to what most users use bcrypt at
(< 0.1 second runtime), it's been shown to be at best no stronger than
bcrypt, or at worst several times weaker.
There is currently a competition for a new password hashing algorithm:
https://password-hashing.net/ I've been watching it fairly closely,
but many of the algorithms suffer a similar "weaker than bcrypt at low
runtime" problem. The main reason for this is that they are mainly
targeting different use-cases than password_hash targets. Namely, they
are looking at key derivation and login where you can dedicate a login
server (and hence keep potentially GB of memory persistent for use in
hashing). While these techniques are far superior if you can dedicate
the hardware to them, they don't solve the same problem that we're
solving with password_hash.
We could use scrypt or yescrypt (or a few others) as a next-gen
algorithm, but we'd have to be careful with the settings parameters,
as there's more than just one. And it's really easy to screw up the
settings and make it easier for an attacker. Ideally, I wonder if it's
possible for us to simply "proxy" from a single setting (give users a
"cost" setting, and generate an pseudo-ideal set of settings given
that cost).
The bottom line is that I think password_hash needs to stay as simple
as possible. It's not designed to be a full fledged library or set of
primitives. It's designed to be an opinionated library that takes as
much decision away from the user as possible. If you need more
control, use a different library. But for the 98% use-case, that
additional complication simply makes it harder for users to get it
right: http://blog.ircmaxell.com/2015/03/thoughts-on-design-of-apis.html
Anthony