15

I am currently learning about cybersecurity and trying to implement it in my next web application.

I have been reading some articles about hashing, specifically SHA2 and Blowfish.

In this article, it is recommended to add a hard-coded salt to passwords so an attacker retrieving the database will have it harder to brute-force a password. This makes sense since the hard-coded salt is not in the database and the attacker won't have access to it (as long as it runs on the server and not in the client).

However, if the salt is hard-coded, that means it is going to be the same for all users, so after the attacker breaks the first password from the database and determines the salt is "123", they can just apply that salt to all brute-force attempts by default.

The first solution for this problem that came to my mind was generating a salt before passing the string to BCrypt, and storing that randomly generated salt in a separate database. The attacker wouldn't have the extra salt if the main database is compromised, and breaking a password wouldn't let you know another user's salt.

However, even if doing this ensures the attacker will not have the extra salt, it might be redundant to add that extra salt before BCrypt, since the point of the salt is to avoid collisions, and BCrypt is already supposed to do that (read the answers here).

This could make sense if hashing the password string with SHA2 before passing it to BCrypt, since after breaking the Blowfish layer of the password from user1 (which is "1234") and the Blowfish layer of the password from user2 (which is also "1234"), you would still need to break the SHA2 layer for their passwords and, since an additional salt is applied, you wouldn't be able to tell they have the same password. Not having the additional salt could difficult the life of an attacker.

I am aware of the potential implementation flaws of hashing with SHA2 before BCrypt described in this question.

Thanks in advance.

Gh0stFish
  • 6,800
  • 1
  • 23
  • 23

1 Answers1

40

What you're talking about with a single shared string being added to all the hashes is usually called a pepper, as opposed to a salt which is unique per hash. The two techniques complement each other, but they're trying to achieve different things:

  • The salt makes cracking multiple hashes more expensive, and protects against pre-computation attacks (such as rainbow tables).
  • The pepper is stored outside of the database, and means that the hashes in the database can't be cracked unless an attacker also compromises something else to obtain the pepper.

In some cases this won't actually matter, but in other cases (such as SQL injection, or finding a database backup) it effectively makes the hashes uncrackable.

However, peppers can be complicated to manage (especially implementing a process to allow you to change the pepper while still retaining access to the old hashes), so they're not particularly common to implement.

An alternative approach would be to encrypt the hashes before storing them in the database - as this can allow you to decrypt and re-encrypt them when you need to rotate your keys.

Using a pepper (correctly) does add an extra layer of security, and protects the passwords in some circumstances. Whether it's worth the additional complexity will depend on the application you're building, and your threat model.

Gh0stFish
  • 6,800
  • 1
  • 23
  • 23
  • Thank you for the answer, after reading the linked article about pepper, I can clearly see the difference now. I have another quick question. Would storing a "pepper version" integer in the database be a good rotation strategy? (i. e., if pepper version is "1", my code knows that the pepper is "1234", if pepper version is "2", my code will know it is "2345"). When a user logs in using a password that is still on pepper version "1", I encrypt and save it with the new updated version "2". I could also do this to all the database at once. – Josep Enric Sendra Serra Oct 17 '22 at 08:31
  • 2
    @JosepEnricSendraSerra if you're adding it to the hash like a salt, then storing the version in the DB and updating on login makes sense (although you'll need to keep all the peppers, because some users don't login often). If you're encrypting the hashes instead, you can just run a script to update every record in the DB at once with the new hash, which will keep your login code simpler. Just make sure you store the old peppers in case you need to restore your DB.. – Gh0stFish Oct 17 '22 at 09:14
  • @JosepEnricSendraSerra _`I could also do this to all the database at once`_. No, since you need the original password to be able to store a version hashed with the new pepper. – jcaron Oct 17 '22 at 16:34
  • 1
    @jcaron I meant applying the pepper to the password. My plan is to generate a BCrypt(14) hash of the password, encrypt it with a pepper (probably AES CBC encryption), where my pepper is my key. I can decrypt all the entries and encrypt them again with a new pepper (aes key) without needing the original password, since I am encrypting the hashes, not the passwords. – Josep Enric Sendra Serra Oct 17 '22 at 19:06
  • What do you call the thing then when you add a byte to the password at hash time and throw it away so you have to make up to 256 attempts to validate a password? That's what I'm used to calling pepper. – Joshua Oct 18 '22 at 15:01
  • 1
    @JosepEnricSendraSerra If you're using encryption, it's not a pepper any more, so stop calling it that. At that point, you and Gh0stFish are describing the same process, and the answer already says that encryption allows you to update everything in one go, as does Go0stFish's comment. – IMSoP Oct 18 '22 at 16:35
  • 1
    @Joshua That sounds like a poor man's attempt at adding a work factor to the hash. There are much better ways to slow down attacks built into modern hashing algorithms. – IMSoP Oct 18 '22 at 16:39