Hi internals!
Recent incidents have shown that even very large websites still don't
get how to do password hashing properly. The sha1 hashes used by
Linkedin et al can be easily cracked even by amateurs without special
hardware.
What could be the reason for this? Why don't people use bcrypt? It is
being recommended already for years, but still most people don't
seem to make use of it.
I think the reason is that it is incredibly hard to use crypt()
correctly, mainly for the following reasons:
- For many people the syntax is hard to grasp. The hashing algorithm
is specified as the salt parameter, which is somewhat non-obvious (at
least for me). - The fact that you verify a password using $hash == crypt($password,
$hash) is equally non-obvious. - Generating correct salts for bcrypt is quite complicated. It is
encoded in some strange base64 format, thus requiring an additional
function to create it. Additionally it isn't particularly easy to
fetch the random bytes for the salt as you have to check several
possibilities for a cross-platform solution (mcrypt initialization
vector, openssl, /dev/*random, mt_rand etc).
Correctly hashing a password with bcrypt thus requires about a hundred
lines of code. So one either has to import a library (and strangely it
seems that people don't like to do that!) or has to roll your own
(usually implementing some part incorrectly...)
Obviously it's somewhat tempting to use a one-liner sha1()
hash
instead of a hundred line bcrypt hash.
So, wouldn't it be better if PHP provided an easy to use API for
secure password hashes natively? So you just have to call a single
function, which magically handles everything for you (like salt
generation).
A simple sample API could be two functions password_hash($password)
and password_hash_verify($password, $hash). But it could just as well
be a fancy, extensible OOP API.
I think this would greatly improve the hashing situation for PHP.
Thanks,
Nikita
Hi internals!
Recent incidents have shown that even very large websites still don't
get how to do password hashing properly. The sha1 hashes used by
Linkedin et al can be easily cracked even by amateurs without special
hardware.What could be the reason for this? Why don't people use bcrypt? It is
being recommended already for years, but still most people don't
seem to make use of it.I think the reason is that it is incredibly hard to use
crypt()
correctly, mainly for the following reasons:
- For many people the syntax is hard to grasp. The hashing algorithm
is specified as the salt parameter, which is somewhat non-obvious (at
least for me).- The fact that you verify a password using $hash == crypt($password,
$hash) is equally non-obvious.- Generating correct salts for bcrypt is quite complicated. It is
encoded in some strange base64 format, thus requiring an additional
function to create it. Additionally it isn't particularly easy to
fetch the random bytes for the salt as you have to check several
possibilities for a cross-platform solution (mcrypt initialization
vector, openssl, /dev/*random, mt_rand etc).Correctly hashing a password with bcrypt thus requires about a hundred
lines of code. So one either has to import a library (and strangely it
seems that people don't like to do that!) or has to roll your own
(usually implementing some part incorrectly...)Obviously it's somewhat tempting to use a one-liner
sha1()
hash
instead of a hundred line bcrypt hash.So, wouldn't it be better if PHP provided an easy to use API for
secure password hashes natively? So you just have to call a single
function, which magically handles everything for you (like salt
generation).A simple sample API could be two functions password_hash($password)
and password_hash_verify($password, $hash). But it could just as well
be a fancy, extensible OOP API.I think this would greatly improve the hashing situation for PHP.
Thanks,
Nikita
Strong +1 on this. I'd suggest writing an RFC.
-- Gwynne
I would definetly like that a lot to be the case. bcrypt is kind'a cryptic
and information about cryptography on the internet is not so informative
and are not in abundance.
Hi!
So, wouldn't it be better if PHP provided an easy to use API for
secure password hashes natively? So you just have to call a single
function, which magically handles everything for you (like salt
generation).
Yes. The fact that crypt()
doesn't allow to generate salt automatically
for non-default algorithms leads to people either defaulting to weaker
md5crypt or doing salt generation manually, which is dangerous. So I
think we need two things here:
-
An RFC for generic password hashing function (probably in hash, but
can be standalone too) improving oncrypt()
). The start with pbkdf2
looks good, but if we could intergate all possible ways it'd be nicer. -
A patch (and RFC if necessary) for
crypt()
to allow it to generate
salts for non-default algorithms.
--
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
Hi internals!
Recent incidents have shown that even very large websites still don't
get how to do password hashing properly. The sha1 hashes used by
Linkedin et al can be easily cracked even by amateurs without special
hardware.What could be the reason for this? Why don't people use bcrypt? It is
being recommended already for years, but still most people don't
seem to make use of it.
The real problem is this:
Writing a login system is a "rite of passage" for every web programmer
and the majority of tutorials out there are using md5()
/sha1() without
salts. Get programmers using trusted, secure systems and we'll start
seeing these problems vanish. This is a social engineering issue, not a
technical one.
http://cubicspot.blogspot.com/2012/05/dear-web-developers-stop-making-login.html
I think the reason is that it is incredibly hard to use
crypt()
correctly, mainly for the following reasons:
- For many people the syntax is hard to grasp. The hashing algorithm
is specified as the salt parameter, which is somewhat non-obvious (at
least for me).- The fact that you verify a password using $hash == crypt($password,
$hash) is equally non-obvious.- Generating correct salts for bcrypt is quite complicated. It is
encoded in some strange base64 format, thus requiring an additional
function to create it. Additionally it isn't particularly easy to
fetch the random bytes for the salt as you have to check several
possibilities for a cross-platform solution (mcrypt initialization
vector, openssl, /dev/*random, mt_rand etc).
You forgot:
-
Prior to PHP 5.3.x, certain key algorithms for bcrypt support were not
necessarily available incrypt()
. -
Some people are not a fan of the output of
crypt()
or the lack of
input controls.
Other thoughts: mt_rand()
does not qualify as random. If you need
random bytes, consider using CSPRNG
(http://barebonescms.com/documentation/csprng/).
--
Thomas Hruska
CubicleSoft President
Barebones CMS is a high-performance, open source content management
system for web developers operating in a team environment.
An open source CubicleSoft initiative.
Your choice of a MIT or LGPL license.
Hi Nikita,
I think you might just get everyone behind this; easily!
However, I'd like to throw in scrypt as well. Thoughts?
Stas has the right approach, not only should the methods be simplified and
platform/algorithm agnostic but have a proper salt built in (there are a
few CSPRNG implementations around), I've seen salts used from numbers to
md5's to just being skipped altogether.
~ Daniel Macedo
Daniel,
However, I'd like to throw in scrypt as well. Thoughts?
Yes, that's something to include for sure. I've actually been working
on the side on an implementation of scrypt to sit next to my pbkdf2
proposal as hash_scrypt (as the native function, so that it can be
used natively (without the salt generation component)...
Stas has the right approach, not only should the methods be simplified and
platform/algorithm agnostic but have a proper salt built in (there are a
few CSPRNG implementations around), I've seen salts used from numbers to
md5's to just being skipped altogether.
Well, just to be clear, a salt does not need a CSPRNG. All it needs to
be is reasonably unique. In fact, I wouldn't make it CS, as that would
deplete the available entropy in the system for CSPRNG generation.
So in practice, a normal PRNG will suffice. With that said, mt_rand()
is not enough. It should be a moderately good PSRNG. It just doesn't
need to be CS. If mcrypt is available, DEV_URANDOM would be a good
place to get entropy.
Or, we could implement a system like I did in
https://github.com/ircmaxell/PHP-CryptLib/tree/master/lib/CryptLib/Random
that follows RFC4086: http://tools.ietf.org/html/rfc4086#section-5.2
Where it mixes together several sources of weak and moderate strength
PRNG...
On another note, I had started an implementation of this yesterday. So
far, I see two "clean" ways of doing it. We could do it class based (I
put it on SPL because it's more of a library addition):
interface \SPL\Password {
public function hash($password);
public function verify($password, $hash);
}
class \SPL\Password\BCrypt implements \SPL\Password {
public function __construct($cost = 15){}
public function hash($password) {
// Work involving crypt()
}
public function verify($password, $hash) {
// Work involving crypt()
}
}
Or, a more procedural approach, with a single "dispatching" function
function password_hash($password, $algorithm = PASSWORD_BLOWFISH,
$options = array()) {
}
function password_verify($password, $hash, $algorithm =
PASSWORD_BLOWFISH, $options = array()) {
}
function password_register($algorithm_name, $hashFunc, $verifyFunc) {
}
The one big issue that I ran into was in registering a namespaced
class into SPL. The SPL class functions aren't designed to handle
namespaced classes as far as I could tell. So we'd have to make a
patch to that first to add macros to support namespaced code...
Thoughts?
Anthony
- snip*
Stas has the right approach, not only should the methods be simplified and
platform/algorithm agnostic but have a proper salt built in (there are a
few CSPRNG implementations around), I've seen salts used from numbers to
md5's to just being skipped altogether.Well, just to be clear, a salt does not need a CSPRNG. All it needs to
be is reasonably unique. In fact, I wouldn't make it CS, as that would
deplete the available entropy in the system for CSPRNG generation.
Whether or not a CSPRNG is needed depends on what you're doing, your
needed level of security. Perhaps add a parameter to control this, so
it would be possible to make use of this function even if you need the
maximum level of security? If it's not available, the function should
fail in some suitable fashion.
snip
Or, we could implement a system like I did in
https://github.com/ircmaxell/PHP-CryptLib/tree/master/lib/CryptLib/Random
that follows RFC4086: http://tools.ietf.org/html/rfc4086#section-5.2
Where it mixes together several sources of weak and moderate strength
PRNG...
Will the entropy multiply by mixing sources? I.e. will the result be
"more random"? Won't it just be as random as the most random source?
Other than that, the SPL version seems like a nice idea.
Regards
Peter
--
<hype>
WWW: plphp.dk / plind.dk
LinkedIn: plind
BeWelcome/Couchsurfing: Fake51
Twitter: kafe15
</hype
Peter,
Whether or not a CSPRNG is needed depends on what you're doing, your
needed level of security. Perhaps add a parameter to control this, so
it would be possible to make use of this function even if you need the
maximum level of security? If it's not available, the function should
fail in some suitable fashion.
For password hashing, it won't ever be needed for the salt. The salt
is not a secret in the context of cryptography. But, on that note, if
we were adding a stronger PRNG generator, it would be good to expose
it natively. And that native exposure would likely take a parameter
for CS-safe PRNG...
Just my $0.02...
snip
Or, we could implement a system like I did in
https://github.com/ircmaxell/PHP-CryptLib/tree/master/lib/CryptLib/Random
that follows RFC4086: http://tools.ietf.org/html/rfc4086#section-5.2
Where it mixes together several sources of weak and moderate strength
PRNG...Will the entropy multiply by mixing sources? I.e. will the result be
"more random"? Won't it just be as random as the most random source?
No, it will not multiply. It can in practice increase from the
strongest source. But it will never be weaker than the strongest
source. The reason for a mixing function like that, is that you're
pulling entropy from multiple sources. So if a single source is
compromised (say mt_rand is compromised with a known seed value), it
doesn't reduce the overall strength of the generated value. If the
strongest source is compromised, it will still be at least as strong
as the next weakest source...
Anthony
Other than that, the SPL version seems like a nice idea.
Regards
Peter--
<hype>
WWW: plphp.dk / plind.dk
LinkedIn: plind
BeWelcome/Couchsurfing: Fake51
Twitter: kafe15
</hype
Peter,
Whether or not a CSPRNG is needed depends on what you're doing, your
needed level of security. Perhaps add a parameter to control this, so
it would be possible to make use of this function even if you need the
maximum level of security? If it's not available, the function should
fail in some suitable fashion.For password hashing, it won't ever be needed for the salt. The salt
is not a secret in the context of cryptography. But, on that note, if
we were adding a stronger PRNG generator, it would be good to expose
it natively. And that native exposure would likely take a parameter
for CS-safe PRNG...
I would say it really depends upon the project. The salt can not only
protect against rainbow tables and password hash collisions, if it is
unknown to an attacker then it essentially acts to further strengthen
the hash by vastly expanding the keyspace. Supposing an attacker is
trying to get at the password for just one user account (say, admin)
and the hashed password is available - if the salt can be
predicted/guessed, then the keyspace is reduced to that of an unsalted
password and you can run a dictionary attack on the hash. If, on the
other hand, the salt is unpredictable and you don't have access to it,
there is no way to run a dictionary attack (offline, that is). The
security here depends upon storage as well, but the point remains - a
salt isn't by default something you can make public knowledge.
It might be a theoretical concern for most people and the people
really wanting the extra level of security would probably know well
enough how to get exactly what they need - but if provisions are made
so you could reuse the same function you might also be able to educate
developers better. I.e. make it easy to do the right thing and more
people will do it.
Regards
Peter
--
<hype>
WWW: plphp.dk / plind.dk
LinkedIn: plind
BeWelcome/Couchsurfing: Fake51
Twitter: kafe15
</hype
Peter,
I would say it really depends upon the project. The salt can not only
protect against rainbow tables and password hash collisions, if it is
unknown to an attacker then it essentially acts to further strengthen
the hash by vastly expanding the keyspace.
That's not true. There are two types of secure algorithms in
cryptography. Those that are kept secure by a secret, and those that
are secure by effort. Hashing is an effort based algorithm. It's
difficult to reverse, so you need to brute force, expending a lot of
effort in the process... Salts are designed to make that brute forcing
non-amortizable, only (meaning that the effort put in to brute force
one hash can't be applied to another). The way they are used in the
algorithms shows that. They are even stored directly with the hash in
a number of implementations (crypt being a key one). That should point
out how the security that a salt adds does not depend on it being
secret.
Supposing an attacker is
trying to get at the password for just one user account (say, admin)
and the hashed password is available - if the salt can be
predicted/guessed, then the keyspace is reduced to that of an unsalted
password and you can run a dictionary attack on the hash. If, on the
other hand, the salt is unpredictable and you don't have access to it,
there is no way to run a dictionary attack (offline, that is). The
security here depends upon storage as well, but the point remains - a
salt isn't by default something you can make public knowledge.
Then it's not a salt. And unless you really know what you're doing,
it's not worth it, and at best you're not making it more secure (and
at worst, you're significantly reducing the security). Remember,
security through obscurity is not security. Check out this post, and
the comments from experts:
http://blog.ircmaxell.com/2012/04/properly-salting-passwords-case-against.html
It might be a theoretical concern for most people and the people
really wanting the extra level of security would probably know well
enough how to get exactly what they need - but if provisions are made
so you could reuse the same function you might also be able to educate
developers better. I.e. make it easy to do the right thing and more
people will do it.
No, if people want the extra level of security, they should then
encrypt the hash with a secure key before storing it. Note that 2
algorithms would be in place, one proof of work, and one dependent
upon a secret. Trying to combine them because one parameter looks like
it could be a secret is not the way to go...
Anthony
I would say it really depends upon the project. The salt can not only
protect against rainbow tables and password hash collisions, if it is
unknown to an attacker then it essentially acts to further strengthen
the hash by vastly expanding the keyspace. Supposing an attacker is
trying to get at the password for just one user account (say, admin)
and the hashed password is available - if the salt can be
predicted/guessed, then the keyspace is reduced to that of an unsalted
password and you can run a dictionary attack on the hash. If, on the
other hand, the salt is unpredictable and you don't have access to it,
there is no way to run a dictionary attack (offline, that is). The
security here depends upon storage as well, but the point remains - a
salt isn't by default something you can make public knowledge.It might be a theoretical concern for most people and the people
really wanting the extra level of security would probably know well
enough how to get exactly what they need - but if provisions are made
so you could reuse the same function you might also be able to educate
developers better. I.e. make it easy to do the right thing and more
people will do it.
May I ask how would you end up at the situation where the attackers have
the password hashes but not the salt?
Any process which needs to read the password hashes will also need
knowledge of the salt. Thus an attacker would most likely also know both.
That's precisely how salts are designed to work.
I admit you could have a common salt for all users stored in php and
only a leak of the database. But such salt would most likely be provided
by the user, generated using a different program... expected to be secure.
Using a shared salt is worse than a uniqe salt per user, so that's not
something to promote either.
You wouldn't be "educating in the right way".
snip
May I ask how would you end up at the situation where the attackers have
the password hashes but not the salt?Any process which needs to read the password hashes will also need
knowledge of the salt. Thus an attacker would most likely also know both.
That's precisely how salts are designed to work.
If your salts are not stored with your password hashes, then an sql
injection that would leak your password hashes would not leak the
salts. The recent database leaks come to mind: had they only contained
password hashes but no salts (I know the hashes were unsalted, but
had they been salted ...) attackers would have faced an impossible
task trying to bruteforce even just one hash. Otherwise, you'd be able
to focus on a given account (an admin account comes to mind) and spend
all your efforts on that using the typical options.
As pointed out once or twice, for most people/purposes, it's a
theoretical discussion. That doesn't mean it shouldn't be taken into
consideration though (people rarely break into your house where you
expect them to).
I admit you could have a common salt for all users stored in php and
only a leak of the database. But such salt would most likely be provided
by the user, generated using a different program... expected to be secure.
Using a shared salt is worse than a uniqe salt per user, so that's not
something to promote either.
You wouldn't be "educating in the right way".
And I'm obviously not advocating a shared salt (at least, I wasn't
thinking I was, especially seeing as I asked for a parameter in
function to make sure that salts would be more random).
Regards
Peter
--
<hype>
WWW: plphp.dk / plind.dk
LinkedIn: plind
BeWelcome/Couchsurfing: Fake51
Twitter: kafe15
</hype
Daniel,
Stas has the right approach, not only should the methods be simplified and
platform/algorithm agnostic but have a proper salt built in (there are a
few CSPRNG implementations around), I've seen salts used from numbers to
md5's to just being skipped altogether.Well, just to be clear, a salt does not need a CSPRNG. All it needs to
be is reasonably unique. In fact, I wouldn't make it CS, as that would
deplete the available entropy in the system for CSPRNG generation.So in practice, a normal PRNG will suffice. With that said,
mt_rand()
is not enough. It should be a moderately good PSRNG. It just doesn't
need to be CS. If mcrypt is available, DEV_URANDOM would be a good
place to get entropy.Or, we could implement a system like I did in
https://github.com/ircmaxell/PHP-CryptLib/tree/master/lib/CryptLib/Random
that follows RFC4086: http://tools.ietf.org/html/rfc4086#section-5.2
Where it mixes together several sources of weak and moderate strength
PRNG...
This:
http://barebonescms.com/documentation/csprng/
Takes a different approach. Generate one or more stored root seeds and
then use those seeds to generate as much data as is needed without
risking loss of entropy. It also accepts extra entropy sources as input
- even weak sources such as an incrementing integer or serialized
user-submitted data - to further enhance the output.
Adding more random sources of information to PHP is a good thing. If
something like what is being discussed is developed, it will merely
become one more source for root seed generation. The more the merrier.
Good sources of entropy are hard to come by.
One thing I would like to recommend is add to the documentation that
certain functions are inappropriate for specific use cases and recommend
alternative solutions. A lot of people out there think rand()
and
mt_rand()
are suitable for security. Maybe have a specific page in the
PHP documentation dedicated to covering random string and number
generation and link to that page from various function pages.
--
Thomas Hruska
CubicleSoft President
Barebones CMS is a high-performance, open source content management
system for web developers operating in a team environment.
An open source CubicleSoft initiative.
Your choice of a MIT or LGPL license.
Thomas,
This:
http://barebonescms.com/documentation/csprng/
Takes a different approach. Generate one or more stored root seeds and then
use those seeds to generate as much data as is needed without risking loss
of entropy. It also accepts extra entropy sources as input - even weak
sources such as an incrementing integer or serialized user-submitted data -
to further enhance the output.
Actually, I would not call that CS. Where's the white paper for the
algorithm? Where's the RFC? Just because you take data from a lot of
sources does not make it CS... And just putting the kitchen sink
into a single sha512 hash does not either. The vast majority of the
data that's being called entropy is purely static on the system (page
to page won't change).
Especially for something that's going into core, I'd suggest sticking
to approved, vetted algorithms. If it doesn't have an RFC or a
reviewed white-paper, I would avoid it.
Additionally, it's pushing all of the "entropy sources" into a single
hash bucket. I'd much rather see it push each one through a hmac round
with the existing data. That way, the relationship between a specific
source and the overall result is complex. It's not just complex in
relation to other data, but also into the algorithm itself...
Adding more random sources of information to PHP is a good thing. If
something like what is being discussed is developed, it will merely become
one more source for root seed generation. The more the merrier. Good
sources of entropy are hard to come by.
Definitely agree...
One thing I would like to recommend is add to the documentation that certain
functions are inappropriate for specific use cases and recommend alternative
solutions. A lot of people out there thinkrand()
andmt_rand()
are
suitable for security. Maybe have a specific page in the PHP documentation
dedicated to covering random string and number generation and link to that
page from various function pages.
Sounds good to me...
Anthony
On Wed, Jun 13, 2012 at 2:31 PM, Nikita Popov nikita.ppv@googlemail.comwrote:
Hi internals!
Recent incidents have shown that even very large websites still don't
get how to do password hashing properly. The sha1 hashes used by
Linkedin et al can be easily cracked even by amateurs without special
hardware.
LinkedIn was using sha1?! Are you fucking serious?? I think it's time for
me to change my password there to something I'm not using anywhere else
lol.
At this rate, tomorrow are we going to learn that Gmail uses md5 and that
Facebook passwords are stored in plaintext files under the HTTP root?....
Anyway, BIG +1 on this RFC!
--Kris
This userland library already solves all the issues you outlined with
bcrypt: http://www.openwall.com/phpass/
The API is very easy to use, has been around for a while and has a
number of detailed articles that explain how to use it.
--
Herman Radtke
hermanradtke@gmail.com | http://hermanradtke.com
hi!
hi!
This userland library already solves all the issues you outlined with
bcrypt: http://www.openwall.com/phpass/The API is very easy to use, has been around for a while and has a
number of detailed articles that explain how to use it.
And that one is already used by many projects as well.
Cheers,
Pierre
@pierrejoye | http://blog.thepimp.net | http://www.libgd.org
Herman,
This userland library already solves all the issues you outlined with
bcrypt: http://www.openwall.com/phpass/
That library is not without its issues. For example, if you ask for a
portable hash, it gives you a custom algorithm instead of bcrypt.
That's because the library is php4 compatible. So for modern versions
of PHP (5.3+), it produces an unnecessarily weak hash.
In addition, the custom hash function that it uses is fairly weak. It
uses md5 as its primitive (because in php4 that's all that's
available), which while not the end of the world, could be improved.
It also does a simple feedback loop on hash the hash function for the
iterations. This is good, but could be better by using hash_hmac which
internally does two hash rounds per call, and feeding back more state
into the loop (Like PBKDF2 does).
It's not bad enough that if you're already using it you should go
running to replace it (not at all). But if you are starting a new
project, stick to bcrypt, PBKDF2, crypt_sha512, scrypt, etc...
If you want some background on it: http://drupal.org/node/1201444
The reason that I am bringing it up, is that if we're pulling it in
core, we should understand the limitations...
Anthony
hi Anthony,
Adding Alex to the loop as his insight will be unvaluable in this thread.
This userland library already solves all the issues you outlined with
bcrypt: http://www.openwall.com/phpass/That library is not without its issues. For example, if you ask for a
portable hash, it gives you a custom algorithm instead of bcrypt.
That's because the library is php4 compatible. So for modern versions
of PHP (5.3+), it produces an unnecessarily weak hash.
Because it was exciting before.
However the point here is not the implementation but the APIs.
To be honest I am not a big fan of providing such an API in the core
as no matter the default implementation, it will become obsolete soon
or later. And changing the default brings its lot of issues and BC
problems.
That being said, it seems that we may not have the choice anyway so
having a well designed and implemented API for password (and related
or similar areas) generations may be a good thing.
Cheers,
Pierre
@pierrejoye | http://blog.thepimp.net | http://www.libgd.org
However the point here is not the implementation but the APIs.
To be honest I am not a big fan of providing such an API in the core
as no matter the default implementation, it will become obsolete soon
or later. And changing the default brings its lot of issues and BC
problems.That being said, it seems that we may not have the choice anyway so
having a well designed and implemented API for password (and related
or similar areas) generations may be a good thing.
The generated password hash should contain versioning information (such
as the $1$ for crypt), sopassword_verify()
of later PHP versions will
be able
to correctly verify it, even after the default password hash changes
(set an older
type in php.ini if you don't want to use the new format).
Hi all,
Adding Alex to the loop as his insight will be unvaluable in this thread.
Thank you for the chance to comment, and sorry that I did not do so yet.
I am busy with lots of other stuff. I'd appreciate it if you don't
hurry to implement this stuff too much, and I likely will comment on it
(that is, on the actual proposed API and implementation). Please keep
me in the loop.
This userland library already solves all the issues you outlined with
bcrypt: http://www.openwall.com/phpass/That library is not without its issues. For example, if you ask for a
portable hash, it gives you a custom algorithm instead of bcrypt.
That's because the library is php4 compatible.
Note: if you ask for a portable hash. What else should it do if you
ask it for just that?
So for modern versions
of PHP (5.3+), it produces an unnecessarily weak hash.
For modern versions of PHP, if you don't require portability of hashes
to older versions, don't ask it for a portable hash.
Of course, new PHP apps that will depend on your new API won't run on
older versions of PHP by definition, so they could assume bcrypt is
available. A concern, though, is that by the time major web apps would
be able to assume a recent enough version of PHP (with this new API)
we'll prefer to use a password hashing method with more than one
configurable parameter (not just one "cost" setting), so we may want to
design the API with that in mind.
Additionally, I was/am going to experiment with implementing a decent
KDF in PHP, but also considering its native reimplementation at the same
time (for inclusion in PHP proper, etc.) That way, we could have hashes
that are both portable (to older versions of PHP, to other scripting
languages, etc.) and efficient. My expectation is that when implemented
in a scripting language (using a widely-available crypto primitive like
SHA-512 or AES), they could be somewhere inbetween bcrypt and scrypt in
terms of cost of attack - and when reimplemented natively, they could be
approaching scrypt (although Colin's choice of Salsa20/8 core for the
crypto primitive was not arbitrary, and unfortunately we don't have that
in PHP). I feel that there's room for exactly one such would-be
de-facto standard "scripting KDF", and I might be in a position to set
this standard due to phpass (this new thing could be a next generation
phpass). But I haven't decided on this yet, I'd need to experiment
first, and I don't like to be rushed. ;-)
http://www.openwall.com/presentations/PHDays2012-Password-Security/mgp00051.html
I think it's OK/preferable not to introduce this new hash type earlier
than in a year from now or so, unless there's imminent threat of someone
else doing it substantially worse. ;-) Meanwhile, I don't mind having
an API that would provide bcrypt, even though I think that phpass does
this well enough (and I intend to release a minor update to this first
generation phpass). As long as we're not introducing a new hash type to
this world yet, we are OK to experiment with APIs, etc.
Alexander
Alex,
Thank you for the chance to comment, and sorry that I did not do so yet.
I am busy with lots of other stuff. I'd appreciate it if you don't
hurry to implement this stuff too much, and I likely will comment on it
(that is, on the actual proposed API and implementation). Please keep
me in the loop.
It's not a huge rush, but at the same time I'd like to get it in by
5.5. The RFC process takes at minimum 3 weeks from official proposal
(2 week discussion + 1 week voting). And I haven't officially proposed
it yet. So there's still some time.
Note: if you ask for a portable hash. What else should it do if you
ask it for just that?
That's a fair point. I guess since the adoption of 5.3, and the fact
that 5.2 is dead (yet alone php4), has me thinking that "portable"
means something different today than it did then...
So for modern versions
of PHP (5.3+), it produces an unnecessarily weak hash.For modern versions of PHP, if you don't require portability of hashes
to older versions, don't ask it for a portable hash.
That's fair. Perhaps the documentation needs updating to indicate that
portable really just means compatibility with php <= 5.2...
Of course, new PHP apps that will depend on your new API won't run on
older versions of PHP by definition, so they could assume bcrypt is
available. A concern, though, is that by the time major web apps would
be able to assume a recent enough version of PHP (with this new API)
we'll prefer to use a password hashing method with more than one
configurable parameter (not just one "cost" setting), so we may want to
design the API with that in mind.
That's a very good point. And it's already designed into the API.
Right now, it only implements one algorithm in the core, but the
switches and options are all there to support multiple. I just didn't
see any solid reason to include the other (slightly less favored)
crypt()
hashes available today (SHA512 for example). But it's designed
so that they can be added easily. I know there's talk floating around
the list with respect to adding scrypt support into core. If that
happens, it would definitely be added to the implementation.
Additionally, I was/am going to experiment with implementing a decent
KDF in PHP, but also considering its native reimplementation at the same
time (for inclusion in PHP proper, etc.) That way, we could have hashes
that are both portable (to older versions of PHP, to other scripting
languages, etc.) and efficient. My expectation is that when implemented
in a scripting language (using a widely-available crypto primitive like
SHA-512 or AES), they could be somewhere inbetween bcrypt and scrypt in
terms of cost of attack - and when reimplemented natively, they could be
approaching scrypt (although Colin's choice of Salsa20/8 core for the
crypto primitive was not arbitrary, and unfortunately we don't have that
in PHP). I feel that there's room for exactly one such would-be
de-facto standard "scripting KDF", and I might be in a position to set
this standard due to phpass (this new thing could be a next generation
phpass). But I haven't decided on this yet, I'd need to experiment
first, and I don't like to be rushed. ;-)
No rush at all. I like the concept though. Would it be worth while to
add an implementation of salsa20/8 to the core?
http://www.openwall.com/presentations/PHDays2012-Password-Security/mgp00051.html
I think it's OK/preferable not to introduce this new hash type earlier
than in a year from now or so, unless there's imminent threat of someone
else doing it substantially worse. ;-) Meanwhile, I don't mind having
an API that would provide bcrypt, even though I think that phpass does
this well enough (and I intend to release a minor update to this first
generation phpass). As long as we're not introducing a new hash type to
this world yet, we are OK to experiment with APIs, etc.
Well, part of the reason for this API, is that it appears that the
majority of developers either aren't willing to use a library, or
don't know it exists (even though it's very easy to find if you try).
Additionally, I would think there would need to be a vetting period
for a new KDF like this (for review, and trial implementations and
such) before it would be considered "stronger" than bcrypt. If that's
the case, then the core implementation would work fine. You implement
the new KDF in a library. It goes through vetting and testing. if all
is good, when it's mature it can be added back into core in this API.
Thanks,
Anthony
So, wouldn't it be better if PHP provided an easy to use API for
secure password hashes natively? So you just have to call a single
function, which magically handles everything for you (like salt
generation).A simple sample API could be two functions password_hash($password)
and password_hash_verify($password, $hash). But it could just as well
be a fancy, extensible OOP API.
I guess SCrypt binding could be implemented.
http://www.tarsnap.com/scrypt.html
That's the best available option at the moment.
http://stackoverflow.com/questions/1226513/whats-the-advantage-of-scrypt-over-bcrypt
It is BSD-licensed, so we can easily bundle it with PHP
For the reference, here's the Python binding: https://bitbucket.org/mhallin/py-scrypt/src
hi,
I guess SCrypt binding could be implemented.
http://www.tarsnap.com/scrypt.html
Using yet another dependency for that? Not good.
That's the best available option at the moment.
http://stackoverflow.com/questions/1226513/whats-the-advantage-of-scrypt-over-bcrypt
This post says the exact opposite, just saying :)
It is BSD-licensed, so we can easily bundle it with PHP
Maybe nice to have in pecl.'
cheers,
Pierre
@pierrejoye | http://blog.thepimp.net | http://www.libgd.org
I guess SCrypt binding could be implemented.
http://www.tarsnap.com/scrypt.htmlUsing yet another dependency for that? Not good.
That's easier and safer than implementing this on our own.
That's the best available option at the moment.
http://stackoverflow.com/questions/1226513/whats-the-advantage-of-scrypt-over-bcryptThis post says the exact opposite, just saying :)
The post says, that SCrypt is better, because it is way harder to solve.
Bcrypt requires a lot of CPU, but SCrypt requires a lot of CPU + a lot of RAM
It is BSD-licensed, so we can easily bundle it with PHP
Maybe nice to have in pecl.'
Sure, that's an option, but pecl won't help php to have default "state-of-art" password hashing toolset ;)
hi,
The post says, that SCrypt is better, because it is way harder to solve.
Bcrypt requires a lot of CPU, but SCrypt requires a lot of CPU + a lot of RAM
Ah right, I read it the other way 'round...
It is BSD-licensed, so we can easily bundle it with PHP
Maybe nice to have in pecl.'
Sure, that's an option, but pecl won't help php to have default "state-of-art" password hashing toolset ;)
There is sadly only state-of-art-right-now password hashing methods.
We have to keep that in mind :)
Cheers,
Pierre
@pierrejoye | http://blog.thepimp.net | http://www.libgd.org
It is BSD-licensed, so we can easily bundle it with PHP
Maybe nice to have in pecl.'
Sure, that's an option, but pecl won't help php to have default "state-of-art" password hashing toolset ;)
There is sadly only state-of-art-right-now password hashing methods.
We have to keep that in mind :)
Sure. but SCrypt is tuneable. One can increase both CPU and RAM complexity and CPU complexity is set as function of time.
Which means, that if one upgrades CPU in his server, while leaving settings the same complexity will increase automatically.
This feature makes it future-proof to some degree. Well… until quantum computers become ubiquitous ;)
Pierre,
There is sadly only state-of-art-right-now password hashing methods.
We have to keep that in mind :)
That's why the crypt()
return format was designed. All of the options
that are needed to validate the hash (algorithm, cost parameter, salt,
etc) are fit right into the outputted string.
I'd suggest that's what's done here. In fact, I'd make the functions
just a thin wrapper around crypt()
. Basically, just where it sets sane
defaults that we can update every minor (or major) release (to
compensate for faster servers). It handles salt generation, error
checking, etc.
Here's what I have in mind in php: https://gist.github.com/2949382
Anthony
2012/6/18 Anthony Ferrara ircmaxell@gmail.com:
That's why the
crypt()
return format was designed. All of the options
that are needed to validate the hash (algorithm, cost parameter, salt,
etc) are fit right into the outputted string.I'd suggest that's what's done here. In fact, I'd make the functions
just a thin wrapper aroundcrypt()
. Basically, just where it sets sane
defaults that we can update every minor (or major) release (to
compensate for faster servers). It handles salt generation, error
checking, etc.Here's what I have in mind in php: https://gist.github.com/2949382
I like your idea to offer a wrapper of crypt()
with a better API
(actually I used this approach in the ZF2 project:
https://github.com/zendframework/zf2/blob/master/library/Zend/Crypt/Password/Bcrypt.php).
I think we should also support the user's salt as option and generate
a random salt if not provided.
For the random generation I suggest to use as first option the
openssl_random_pseudo_bytes()
that is considered more secure compared
with mcrypt_create_iv($size, MCRYPT_DEV_URANDOM).
I just wrote that changes here: https://gist.github.com/2949592
Regards,
Enrico Zimuel
Enrico
I like your idea to offer a wrapper of
crypt()
with a better API
(actually I used this approach in the ZF2 project:
https://github.com/zendframework/zf2/blob/master/library/Zend/Crypt/Password/Bcrypt.php).
Yeah, crypt()
is really nice, and offers a lot of good things out of
the box. It's just the salt generation and error handling that are a
pita...
I think we should also support the user's salt as option and generate
a random salt if not provided.
Yeah, that could be good. The only part we'd need to be careful of is
error checking the salt (correct length, correct format, etc). But in
general I like the idea...
For the random generation I suggest to use as first option the
openssl_random_pseudo_bytes()
that is considered more secure compared
with mcrypt_create_iv($size, MCRYPT_DEV_URANDOM).
Well, the point wasn't to make a CS secure salt. We've already had
discussions on that (it's not needed, and can harm the system to try
to use CS salts). All salts need to be is unique (statistically unique
is usually good enough). If people really want to use a CS salt, they
should pass one in as the user-generated salt. But I'd really like to
voice my opposition to having this function default to CS secure salt
generation...
I just wrote that changes here: https://gist.github.com/2949592
Looks good to me otherwise.
I expanded my original gist a bit to add in the ability to register
your own algorithm:
https://gist.github.com/2949382
That way, existing projects that use things like PHPASS can register
their own handler, and move towards this (let the fallback happen in
password_validate instead of in user code).
If we're going to go with this option (the series of functions), what
do you think of putting them under spl:
\SPL\password_create()
\SPL\password_validate()
\SPL\password_register_algo()
\SPL\password_create_salt()
Instead of doing it class based as was originally suggested?
The reason for namespacing is that password_validate is used in the
wild (not by many:
http://www.koders.com/default.aspx?s=%22password_validate%28%22&search.x=0&search.y=0&la=PHP&li=*&scope=
)
Anthony
Hi Anthony,
I'm not sure about the idea to have a specific SPL for password hashing.
I like the idea to have a standard interface for that but maybe is
enough just a set of functions.
I see a valid argument to have a standard interface especially for the
password_register_algo().
Enrico
2012/6/18 Anthony Ferrara ircmaxell@gmail.com:
Enrico
I like your idea to offer a wrapper of
crypt()
with a better API
(actually I used this approach in the ZF2 project:
https://github.com/zendframework/zf2/blob/master/library/Zend/Crypt/Password/Bcrypt.php).Yeah,
crypt()
is really nice, and offers a lot of good things out of
the box. It's just the salt generation and error handling that are a
pita...I think we should also support the user's salt as option and generate
a random salt if not provided.Yeah, that could be good. The only part we'd need to be careful of is
error checking the salt (correct length, correct format, etc). But in
general I like the idea...For the random generation I suggest to use as first option the
openssl_random_pseudo_bytes()
that is considered more secure compared
with mcrypt_create_iv($size, MCRYPT_DEV_URANDOM).Well, the point wasn't to make a CS secure salt. We've already had
discussions on that (it's not needed, and can harm the system to try
to use CS salts). All salts need to be is unique (statistically unique
is usually good enough). If people really want to use a CS salt, they
should pass one in as the user-generated salt. But I'd really like to
voice my opposition to having this function default to CS secure salt
generation...I just wrote that changes here: https://gist.github.com/2949592
Looks good to me otherwise.
I expanded my original gist a bit to add in the ability to register
your own algorithm:https://gist.github.com/2949382
That way, existing projects that use things like PHPASS can register
their own handler, and move towards this (let the fallback happen in
password_validate instead of in user code).If we're going to go with this option (the series of functions), what
do you think of putting them under spl:\SPL\password_create()
\SPL\password_validate()
\SPL\password_register_algo()
\SPL\password_create_salt()Instead of doing it class based as was originally suggested?
The reason for namespacing is that password_validate is used in the
wild (not by many:
http://www.koders.com/default.aspx?s=%22password_validate%28%22&search.x=0&search.y=0&la=PHP&li=*&scope=
)Anthony
Pierre,
There is sadly only state-of-art-right-now password hashing methods.
We have to keep that in mind :)
That's why thecrypt()
return format was designed. All of the options
that are needed to validate the hash (algorithm, cost parameter, salt,
etc) are fit right into the outputted string.I'd suggest that's what's done here. In fact, I'd make the functions
just a thin wrapper aroundcrypt()
. Basically, just where it sets sane
defaults that we can update every minor (or major) release (to
compensate for faster servers). It handles salt generation, error
checking, etc.Here's what I have in mind in php: https://gist.github.com/2949382
I don't think the code is the most appropiate one, but I suppose that's
not a final proposal.
The interfaces look good to me.
I'd maybe set the default $algo to PASSWORD_DEFAULT_HASH or similar,
being a value bumped on each next revisions.
I would consider preferable to have the $ ofpassword_register_algoprefix
implicit.
Regards
Angel,
I don't think the code is the most appropiate one, but I suppose that's
not a final proposal.
Correct, it was just to fill out the interface a bit so that people
could play with it and see how the interface worked...
The interfaces look good to me.
I'd maybe set the default $algo to PASSWORD_DEFAULT_HASH or similar,
being a value bumped on each next revisions.
Yeah, that's a good idea. Makes it easier to update without changing
the interface.
I would consider preferable to have the $ ofpassword_register_algoprefix
implicit.
That's fair.
Thanks,
Anthony
Hello again,
I have put together a quick proof-of-concept for the password API.
C:
https://github.com/ircmaxell/php-src/blob/hash_password/ext/standard/password.c
PHP (Implemented as a backwards-compatibility layer, so 5.3/5.4 users
can use the API earlier):
https://github.com/ircmaxell/password_compat/blob/master/lib/password.php
It needs some work before it's ready for an RFC, but it works as intended.
Right now, it defines 3 functions:
password_create($password, $algo = PASSWORD_DEFAULT, array $options = array())
password_verify($password, $hash)
password_make_salt($length, $raw_output = false)
Some of the things I want to change include the ability to set the
default bcrypt and sha* cost/round parameters via a php.ini setting
(right now, it's just a c constant). I also want to refactor it a bit
to clean it up. I also need to test it out fully... I also don't care
for the amount of PHP function calls it makes
(zend_call_method_with_n_params). But that may be ok, given the
What do you think so far?
Anthony
Angel,
I don't think the code is the most appropiate one, but I suppose that's
not a final proposal.Correct, it was just to fill out the interface a bit so that people
could play with it and see how the interface worked...The interfaces look good to me.
I'd maybe set the default $algo to PASSWORD_DEFAULT_HASH or similar,
being a value bumped on each next revisions.Yeah, that's a good idea. Makes it easier to update without changing
the interface.I would consider preferable to have the $ ofpassword_register_algoprefix
implicit.That's fair.
Thanks,
Anthony