22

I would like to allow users of my web application to have long passwords, if they so wish. Today I became aware of bcrypt's password length limitation (72 characters, the rest truncated).

Would it be secure for me to do the following? I am using PHP.

Current Implementation:

password_hash($password, PASSWORD_BCRYPT, $options);

Implementation in question:

password_hash(hash('sha256', $password), PASSWORD_BCRYPT, $option);

What are the drawbacks of the implementation in question?

I am not a crypto expert, please advise.

Will the implementation in question limit the password length that a user can use? If so, what will the limit be?

3

3 Answers 3

26

In general, this will be fine. It's even a fairly well recommended method to allow arbitrary length passwords in bcrypt.

Mathematically, you are now using bcrypt on a 64 character string, where there are 2^256 possible values (SHA-256 gives 256 bit output, which is commonly represented as a 64 character hexadecimal string). Even if someone pre-calculated common passwords in SHA-256, they'd need to run those through bcrypt to find what the actual input for a given hash was.

The main potential drawback is implementation flaws: if you ever store the SHA-256 values, they're relatively fast to break, so an attacker wouldn't need to expend the effort to break the bcrypt values.

It would still be recommended to keep a high enough iteration count for the bcrypt step - this shouldn't have any particularly detrimental effect on your processing time, but makes brute force attacks much harder.

See Pre-hash password before applying bcrypt to avoid restricting password length for the general case.

5
  • Thanks a lot! When it then comes to user login, do I just perform the usual password_verify($posted_password, $db_stored_hash), or would be I correct in assuming that I should throw an SHA-256 hash into the verification process too? So I am thinking it should be password_verify(hash('sha256', $posted_password), $db_stored_hash), is thinking correct?
    – Jay Welsh
    Feb 14, 2017 at 10:19
  • 1
    You'd need the hash in there, yes. password_verify will just compare the input and bcrypt hash, so the input to it needs to be whatever was hashed at that point.
    – Matthew
    Feb 14, 2017 at 10:29
  • 1
    Salt all the things! :) Feb 14, 2017 at 19:31
  • 2
    @StenPetrov Oddly enough, you don't need to add any more salt to that which bcrypt provides - the purpose is to mask identical passwords, which bcrypt's salt does perfectly well! No harm in adding it to the SHA-256 hash as well, but you don't need to.
    – Matthew
    Feb 14, 2017 at 19:40
  • 1
    @Matthew yes, I was referring to SHA, for the reason you pointed out - precomputed SHA hashes... in this case the added protection wouldn't be very significant since SHA is fast but I'd add it nevertheless Feb 14, 2017 at 22:22
12

Your suggested implementation is safe. However take care to document your special usage of the function.

Please note that password_hash will truncate the password at the first NULL-byte so you must NOT use the option $raw_output=true of the hash function.

4

Don't do it, you are limiting your key strength to much less than what bcrypt alone can accept.

Use this approach instead (pseudo-code not valid PHP):

if length($password) > 72 bytes
then
    $hash = sha256($password)
    $password = concat(substring($password, 320 bits), base255encode($hash))
end if
bcrypt($password)

In this way, passwords between 256 and 576 bits retain their full entropy, and passwords of above 576 bits use not only the 256 bits from the hash, but as much of the original entropy as can fit alongside the hash.

It's debatable whether the hash should be done across the whole password, or just the portion beyond 320 bits that the hash replaces. I seriously doubt it makes any difference when you've chosen a cryptographic hash with good properties.

The base255encode step avoids having a NUL byte terminate the key early, as @a-hersean warned about.

9
  • This seems unnecessarily complicated. Are 256 bits not enough?
    – Ry-
    Feb 15, 2017 at 2:16
  • 4
    256 bits are not enough for someone paranoid enough to use a password longer than 72 bytes.
    – Ben Voigt
    Feb 15, 2017 at 2:26
  • Well, they don’t have to know…
    – Ry-
    Feb 15, 2017 at 2:35
  • @BenVoigt I agree with the principle of your comment. But you could well have a >72B password and a non-paranoid user. If you consider a sentence style password with a few unicode chars thrown in (e,g, the sentence is in a language that doesn't work in ASCII) you could actually hit 72B.
    – Chris H
    Feb 15, 2017 at 9:31
  • 2
    If you want more than 256 bits, why not just use base255encode(hash('sha512', password, true)) instead of this complicated scheme above? As the entropy of the password is not guaranteed to be spread evenly across the individual characters, taking an arbitrary substring doesn't seem very safe to me.
    – wrtlprnft
    Feb 15, 2017 at 12:21

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .