1

I'm working on a new web app, and I chose Bcrypt to hash the passwords. To overcome the 72 byte limit on Bcrypt, I'm planning to pre-hash the passwords with SHA-256. However I researched a little and found out this isn't recommended because of "Password shucking". I read about this on multiple websites for an hour, and can't I just prevent this by salting the pre-hash?

So instead of bcrypt(sha256(password)), I would do bcrypt(sha256(password + salt)).

If I got it right password shucking works by attacker getting unsalted hashes from somewhere else, hashing them with Bcrypt and comparing them to see which passwords are still used, then cracking those hashes. I don't think if I salt the pre-hash they can do this comparision, but I'm not too good at security, so I'm asking this here.

Also for the salt, is using the username or something else unique to that user as a salt, instead of a static one a good idea?

2
  • I think you may be over-complicating this. Most implementations of bcrypt will simply truncate the input password to 72 bytes, if the password provided is longer than this. Also most bycrypt implementations will by default generate a random salt which is hashed with the password, to prevent the attack that you describe. This random salt is usually stored with the rest of the bcrypt output, so that it is available when needed for password verification.
    – mti2935
    Jul 16, 2023 at 21:50
  • I don't want to have a low password length limit, and according to some sources passwords shouldn't get truncated.
    – enxg
    Jul 16, 2023 at 21:56

1 Answer 1

1

can't I just prevent [password shucking] by salting the pre-hash?

Yes.

To understand how password shucking works, and how to prevent it, consider the following hypothetical scenario:

Suppose a user uses a password that is hard (but not impossible) to crack such as: 0e6d5b95c11d. The user uses this same password on both your site and another site.

The SHA256 hash of this password is: 31903c9394eb17e176898d31b2ac06d0cfd04b077192341f8e8f3b5866ea0da2.

The other site hashes passwords by simply using unsalted SHA256, i.e. sha256(password), so that site stores 31903c9394eb17e176898d31b2ac06d0cfd04b077192341f8e8f3b5866ea0da2 for this user in its password database.

Now suppose your site hashes passwords using bcrypt(sha256(password)). This effectively means that your site would be storing bcrypt(31903c9394eb17e176898d31b2ac06d0cfd04b077192341f8e8f3b5866ea0da2) for this user in your password database.

Now suppose your site and the other site are both breached. The attacker sees that your site uses bcrypt(sha256(password)) as your password hashing algorithm, so he runs all of the unsalted SHA256 hashes from the other site (even though these are not yet cracked) through bcrypt to see if any of them appear in your password database. Lo and behold, when he gets to 31903c9394eb17e176898d31b2ac06d0cfd04b077192341f8e8f3b5866ea0da2, he finds the result in your password database.

Now, the attacker can concentrate his resources on cracking the unsalted SHA256 hash 31903c9394eb17e176898d31b2ac06d0cfd04b077192341f8e8f3b5866ea0da2. Cracking a single iteration of unsalted SHA256 is much easier for the attacker than cracking bcrypt, so this effectively removes any added security that bcrypt provides. This is how password shucking works.

But, none of this would be possible if you used bcrypt(sha256(password + salt)), because the attacker would presumably not have sha256(password + salt) from another breach, assuming that the salt that you are using for the SHA256 'prehash' is globally unique for each user.

To ensure that the salt used in the prehash is globally unique for each user, it's best to use a CSPRNG to generate a random salt for each user. This means that you'll need to store the prehash salts in your password database for each user.

5
  • Would using the bcrypt salt for SHA256 aswell be a problem?
    – enxg
    Jul 17, 2023 at 17:22
  • Interesting idea. I can't think of a reason why using the same salt for both the SHA256 'prehash' and the bcrypt 'secondary' hash would be a problem, other than the fact that this starts to tread towards 'rolling your own crypto', which is frowned upon in the security community. But, it might be worth asking in a separate question, as someone else on here might know of a more specific reason why this shouldn't be done.
    – mti2935
    Jul 17, 2023 at 18:45
  • See the blog post that I linked to in my answer. To prevent password shucking, it may be enough to simply pepper the password in the prehash, using a constant string, like 'bcrypt-prehash'. For example, bcrypt(sha256('bcrypt-prehash' + password)).
    – mti2935
    Jul 17, 2023 at 18:52
  • Thanks for the help, I'm probably just going to use a static one like you suggested, since Bcrypt already uses a random hash so there can't be 2 users with the same hash.
    – enxg
    Jul 17, 2023 at 19:02
  • I meant random salt*
    – enxg
    Jul 17, 2023 at 19:08

You must log in to answer this question.

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