I recently became aware of the fact that bcrypt truncates passwords to 72 characters. Practically speaking my intuition is that this does not pose any major security problems. However, I understand that it does mean any software libraries that use bcrypt potentially suffer from a "bug" where two ultra-long passwords that begin with the same 72 characters will be equivalent.

The authors of the Django web framework wrote something called BCryptSHA256PasswordHasher to resolve this issue; what it does is first hash users' passwords with SHA256 before passing them to bcrypt.

Some devs on my team were debating the merits of this approach and I just wanted to collect thoughts from the general community. On the one hand, does this not in some sense reduce security by shrinking the total space of possible values being fed into bcrypt? SHA256 will always output 32 bytes, far short of the 72 (significant) bytes that we would have otherwise. On the other hand, without hashing passwords first we essentially have an unevenly distributed space where for every 72-character prefix, all passwords starting with that prefix collide.

My gut feeling is that the distribution issue past 72 bytes is unimportant and that BCryptSHA256PasswordHasher really isn't useful. That said, I also recognize that, generally speaking, frameworks that are as popular and widely-used as Django tend to take these things seriously and have good reasons behind their decisions. So I don't really have much confidence in my gut on this one, if that makes sense.

Am I wrong? Does hashing passwords with SHA256 before bcrypt not reduce security, even theoretically? Is the distribution problem more severe than I realize? Is there something else that I'm just completely not thinking of?

  • SHA256 outputs are 256-bit (32 bytes like you said). This level of entropy is more than anyone could foreseeably break even with large GPU farms. Even at 1 trillion passwords a second, 2^256 is so large that it would still take 2^216 seconds (that is orders of magnitude longer than the universe has existed) to attempt every password. Just judging on the reduction from 72 bytes of entropy to 32 bytes, this wouldn't increase the likelihood that the password would be brute forced. Also I have to mention that any human generated 72 char password wouldn't really have 72 bytes of entropy anyway. – Owen Jun 22 '15 at 15:15
  • @Owen I understand that. But the question still remains (to me) what the point of hashing with SHA256 is in the first place. Does it solve a real security problem? Also, I share your skepticism regarding human-generated 72+ char passwords; but I am also skeptical that any 72+ char passwords would be human-generated (as opposed to tool-generated) at all. – Dan Tao Jun 22 '15 at 15:20
  • 3
    if we are talking machine generated (pseudo)random passwords longer that 72 characters, then the likelihood that 2 of them would be the same in the first 72 positions is even less likely than a brute force break I mentioned above. While this does reduce the entropy of the input to bcrypt, I wouldn't say it actually reduces security. – Owen Jun 22 '15 at 15:33
  • I agree with @Owen. Maybe they did that just to higher the computation time ? – r00t Jun 22 '15 at 15:37
  • @Owen Right. In practical terms I see no difference worth any consideration between these two approaches (i.e. hashing w/ SHA256 before bcrypt or not). I guess what I'm still failing to see is whether hashing the password first actually accomplishes anything useful. From a simple engineering standpoint, when faced with the choice of either doing something or not doing it, shouldn't there be a compelling benefit to doing it? – Dan Tao Jun 22 '15 at 15:38

Bcrypt as stated in the Link is limited to 72 Characters.

SHA256 may have an OUTPUT size of only 32 Bytes, It's Message input is ((2^64)-1)\8 or roughly

2305843009213693952 Bytes (assuming a char is 8 bits)

To Bcrypt it's receiving a 32 Byte passphrase to encrypt, To SHA256 that could be a 400 Char data stream (IE password).

So no, you're not losing entropy on this, you're overcoming a limitation in Bcrypts design (If you want to call it a limitation.

Link on SHA2 discussing is sizes: https://en.wikipedia.org/wiki/SHA-2

| improve this answer | |
  • 2
    I can't follow your argument: the key space for SHA-256 is still of (roughly) 32 bytes: beyond that, you're guaranteed to have a collision. This means that, even if you limit your input to 7-bit ASCII and considered that SHA-256 was a perfect hash function, you'd still have 2^248 collisions if you hash the passwords before feeding it to bcrypt. Am I missing something ? – Stephane Jun 23 '15 at 7:44
  • Since I am not really great a explaining the Math behind this all I will provide some links. stackoverflow.com/questions/4676828/… At this point were just talking about the SHA256 function ignoring Bcrypts additional layer. – Shane Andrie Jun 23 '15 at 16:34
  • I don't think the existence of a collision in SHA-256 is a problem, the question is, how easy will it be to create one. It is very likely is that two passwords that differ after the 72th character will have different SHA-256 values, so if that is the main concern it will help. If not: The hash-before-bcrypt approach will weaken the overall security against brute-force if the number of bits (here 256) is smaller than the relevant number of bits without. This is 72*8=576 in theory (bcrypt), but if the entropy is 6 bits per character (a good password) then you have 72*6=432 bits which is higher. – Ned64 Apr 2 '17 at 10:00

According to Django's documentation it's because

password are truncated on the first NULL byte (if any) and only the first 72 bytes of a password are hashed... all the rest are ignored.

They acknowledge that it's not a security issue:

While not a security issue per-se, bcrypt does have one major limitation (see previous)

Also, they note that the end of the 72C passwords are not "as important as" the rest:

Furthermore, bytes 55-72 are not fully mixed into the resulting hash (citation needed!).

I could translate it by "the end of the password is easier to crack than the begining".

That's why they do a has first (which as not NULL character, and is less than 72 characters and has no 55-72 characters):

To work around both these issues, many applications first run the password through a message digest such as SHA2-256. Passlib offers the premade passlib.hash.bcrypt_sha256 - BCrypt+SHA256 to take care of this issue.

This improves security if you have long passwords to handle. This may slightly decrease it if you only have short usual (A-Za-z0-9 & special chars from keyboard) passwords because of potential collisions (meh, not that important). So for a generic library, it makes sense to hash. For a lambda website/application managing user password, it seems useless (you have more risks by doing it wrong than by not doing it at all).

| improve this answer | |

I think the use of hashing the password prior to sending it to bcrypt is to overcome bad password habits. As long as you're generating random passwords then using the hash will be a detriment.

Let's say that your password is . So now I can have my 72 characters saved in a document and copy/paste it then append the name of the computer it's on (or some other calculation that is easy for a human to do). As we see this will result in essentially all of them being the same password. By hashing it we can prevent this, but at the cost of keyspace because the particular algorithm we're using outputs 32 characters.

Now, let's assume that every time you set a password it is randomly generated, the whole thing. A 72 character password has (9572) combinations. Your chance of collision is 1/9572.

If we hash this random value we will get 1 of 9532 values and thusly our chance of collusion is 1/9532. So already we can see that hashing costs us some security.

There's more to it though. If we consider that the hash will always output a 32 character string, even if the input is less than 32 characters, then we have exactly 9532 possible passwords. But bcrypt will take passwords in a range of lengths (31, 55, etc). So you can count those as well (9572+9571+9570+9569...) which quickly add up, resulting in further disparity between the two methods.

That's the theoretical, but what about the practical. How many characters do we need for suitable security? If it would take longer than the amount of time until the heat death of the universe to find a collision in a 32 character hash (I don't know that to be a fact, just making an example) then we have squeezed all the security we can out of that. We gain nothing but warm fuzzy feelings by making passwords longer. However, we can add security via hashing which helps mitigate bad password practices (when they're longer than 72 characters).

| improve this answer | |
  • I don't think an attacker would try to feed bcrypt 32 byte binary strings - she would feed SHA-256 common passwords. So knowing the lenght of the input isn't really an issue here. – Anders Apr 6 '17 at 14:25
  • Also, I think your second paragraph is incorrect. In this case you are much more likely to get collisions if you don't hash. The risk of getting an SHA-256 collision is effectively zero. – Anders Apr 6 '17 at 14:27
  • @Anders If you take every possible 72 character string and hash it down to 32 characters then you WILL get collisions. There are just not enough characters to represent all possible inputs uniquely. – tenmiles Apr 7 '17 at 13:03
  • Yes, off course. But unless you have an astronomical number of users, the chance of that ever happening is astronomically small. – Anders Apr 7 '17 at 13:05
  • I see a lot of talk (above) about using bcrypt to enhance the security of the password, but I don't buy it. If you have 72 characters to play with then a random 72 character string is the best you're going to do. Hashing it down to 32 characters not only reduces the length of the string (and therefore strength), but also introduces a weakness whereby a large subset of those possibilities can be excluded. On paper the 72 character string is stronger, whether the inferiority of the hashed password has any practical significance in today's world is not what I was trying to address. – tenmiles Apr 7 '17 at 13:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.