3

I've been reading about how it works and it's really cool in how it slows down bruteforce attempts however it still doesn't feel secure.

Lets say someone stole my database data including all my user password hashes, and knows that I used password_hash to hash my passwords. Can't he just loop through my passwords with his dictionary and password_verify to gain access?

Is it good practice to add another salt before hashing the password?

Khalid Al-Mutawa
  • 829
  • 1
  • 9
  • 16

3 Answers3

4

No, if someone stole the database, they would probably already have access to everything, but say they didn't, all they would get is the hashes.

The hash can't be used in the password field, as it would be hashed again and not match the hash in the database, one would need the actual password, not the hash.

Using password_verify doesn't give you the original password, it checks wether or not a password matches the hash only, which means you would still have to have the original password and the hash to see if they match, so having only the hash gets you nowhere.

$is_correct = password_verify($password_typed_by_user, $hash_gotten_from_db); // bool

At the end of the day, nothing is secure, the hash could most likely be broken with brute force, rainbow tables or with wordlists/dictionaries etc. but that's irrelevant as it generally takes a lot of time and effort and can be done with any hash regardless of wether or not password_hash was used or not.

Using an updated hash and a strong password is the best defence, if the algorithm used to hash the password is somewhat slow, it takes longer to loop through a wordlist and hash each word. Likewise if the password is long or has special characters, it would take longer to check all the characters up to that length etc.

Adding more salts does nothing. The salt isn't a secret, it's just a custom thing you add to avoid having the hashes cracked with precomputed lookup tables, like rainbow tables.

The salt is usually stored with the hash, either in the same database in a different field, or when using PHP's password_hash it's actually just concatenated to the hash, looking something like mysalt.hash.
In general one should use random salts, to make it impossible to pre-generate tables of hashes, and that's all that's needed, the salt isn't a secret and it doesn't add security other than making the function to generate the hash somewhat unique so it can't be duplicated on a mass scale.

adeneo
  • 312,895
  • 29
  • 395
  • 388
  • If he has both the hash and dictionary locally, can't he just use `password_verify` to check if the *guessed password* equals the hash? - would adding an additional salt before hashing improve it? – Khalid Al-Mutawa Apr 05 '15 at 16:49
  • He could, but that can be said of any system that stores hashes and salts, and if the password isn't in the dictionary it would fail. In other words, having good dictionaries and a lot of computer power speeds up cracking the password. Having long passwords that don't consist of common words, phrases and passwords, that also contains a large set of characters, slows down the process of cracking the password. – adeneo Apr 05 '15 at 16:52
4

Adding to the answer by @adeneo,

The point of bcrypt, pbkdf2, scrypt and modern password hashing strategies is to be slow.

Yes, if you get the resulting hash from the database (SQLi) you can just try passwords and attempt to verify each one.

However, just try passwords is a bit of an understatement. Let's look at some math.

The default cost of password_hash() with bcrypt takes approximately 0.1 seconds to hash a password. So that means it takes about 0.1 seconds to verify a password hash.

There are about 1,000,000 words in the english language. To try each one, you'd need to verify 1,000,000 times. At 0.1 seconds per, that's 100,000 seconds (~27 hours).

27 hours for 1,000,000 guesses for a single password hash that was leaked. Since each hash comes with a salt, an attacker would need to repeat these guesses for each leaked hash.

If your database had 1 million users, just to try the dictionary against the passwords would take 76,000 CPU-years (1 CPU for 76,000 years, 76,000 CPUs for 1 year, or any trade between).

To put that in perspective, md5() on a 25 GPU cluster can do about 180,000,000,000 guesses per second. To check those million dictionary entries against the million hashes from the DB, it'd take about 5.5 seconds.

137 GPU-seconds vs 76,000 CPU-years. That is why bcrypt is used.

ircmaxell
  • 163,128
  • 34
  • 264
  • 314
2

That is always a problem. Let's hope your users used passwords that aren't in a dictionary or in the top 100 used passwords. It's your responsibility to enforce stronger passwords that are not easily found in a dictionary.

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
  • 1
    There is only one way to create a strong password: 1. Open your editor 2. Hit your head 1-3 times on your keyboard and you're good to go :) – Rizier123 Apr 05 '15 at 16:48
  • Would adding another salt on server-side improve this? – Khalid Al-Mutawa Apr 05 '15 at 16:51
  • @KhalidAl-Mutawa Only if that salt is stored somewhere else and an attacker has no way to steal that too. – Artjom B. Apr 05 '15 at 16:53
  • The salt isn't a secret, and it shouldn't be, it's mostly to make it almost impossible to do attacks with rainbow tables and precomputed hashes. – adeneo Apr 05 '15 at 16:55
  • 1
    @Rizier123 Joking aside (not really), if you have a pointy head then you won't hit enough keys to make that password long enough. If on the other hand you have a flat head, you run into a problem because will also hit adjacent keys and those patterns can be exploited by password crackers. – Artjom B. Apr 05 '15 at 16:56
  • "‘Salt’ is the bare minimum a reasonable password storage scheme needs, but it does not necessarily make a secure password storage scheme." http://chargen.matasano.com/chargen/2015/3/26/enough-with-the-salts-updates-on-secure-password-schemes.html – John McMahon Apr 05 '15 at 16:57