9

I'm building a site that has users on them, and as with most sites housing some user-type system, they sign in with their e-mails and passwords. I'm using PHP for the back-end portion of my site.

After reading up some articles and posts on the Internet, I learned of the PHP functions password_hash() and password_verify() and wanted to know if an example procedure such as this one was secure enough?

  1. Register the user, password_hash() their password and store the hash in the database.
  2. When logging in, use password_verify() to verify the password and log them in.
  3. If they want to change their password, get their input and password_hash() the input again.

So the questions I have with this are as follows:

  1. Are password_hash() and password_verify() the only functions I need?
  2. Can I take raw user input and safely hash a password using password_hash() for storage in a database?

Any answers would be greatly be appreciated. Thank you.

Gumptastic
  • 145
  • 2
  • 8
  • 2
    1.Yes and 2.Yes. `password_hash` is the most powerful at this time. You do not need to add salt or pepper. Just `password_hash` is enough. I am sure. – Ngô Văn Thao Aug 15 '15 at 03:36
  • some light reading on the issue: http://stackoverflow.com/questions/401656/secure-hash-and-salt-for-php-passwords?rq=1 –  Aug 15 '15 at 03:38
  • @DeDee the password_hash function has built in random salt. There is no need for additional salt. It would in fact be counterproductive. – Mike Brant Aug 15 '15 at 03:39
  • @MikeBrant That was just what I was about to say. I always took the random salt for granted and felt it worked well for my purposes. – Gumptastic Aug 15 '15 at 04:05
  • Oh, nevermind the salt thing then :) Deleted my first comment. – DeDee Aug 15 '15 at 11:53

2 Answers2

6

Yes to both questions, with a couple of caveats:

  1. Bcrypt truncates after 72 characters. While this doesn't degrade security in any practical way, it still leaves a bad taste in some peoples' mouths. People typically work around this by passing it through a hash function like so (please read the second caveat!):

    password_hash(hash('sha512', $_POST['password'], true), PASSWORD_DEFAULT);
    
    password_verify(hash('sha512', $_POST['password'], true), $storedHash);
    
  2. Bcrypt breaks on NUL bytes, which means that if the SHA512 hash of your password begins with 00, an attacker cracking the hashes will see it as the bcrypt hash of an empty string.

If you want to accept passwords longer than 72 characters without silent truncation, do what we did in password_lock: base-64 encode the raw hash output during hashing AND verification.

(The authenticated encryption of the password hash that password_lock performs is optional, and only increases security if your database is on separate hardware from the webserver.)

But if you're just accepting user's passwords, bcrypt is fine. Most people's 72 character passwords are unbreakable anyway.

Scott Arciszewski
  • 33,610
  • 16
  • 89
  • 206
  • I read somewhere (I'll have to find the link later) that even if the password is longer than 72 characters, it still won't be hard to decrypt. Would it be worth the effort doing all the extra hashing when the chances of a hacker uncovering the hash of a 72+ character string are tremendously improbable? – Gumptastic Aug 15 '15 at 04:11
  • It depends on the quality of the password. If we assume all possible keyboard characters, 72 characters means 94^72 possible passwords, or [471.9 bits of entropy](https://www.google.com/search?q=lg(94^72)). It will be fine, 128 bits is considered uncrackable. – Scott Arciszewski Aug 15 '15 at 04:17
  • Yeah, I have certain rules on my site, enforcing them to have at least one special character, a digit, an uppercase letter, etc.. I think I'll be fine. – Gumptastic Aug 15 '15 at 04:23
3

password_hash() and password_verify() is enough when you use these functions with BCrypt algo. BCrypt is the most secure algo now a days to hash passwords. BCrypt is the default algo as of php 5.5.0 so you can use PASSWORD_DEFAULT to use BCrypt. BCrypt requires 5.3.7 or greater version of php. password_hash() generates salt automatically for every password, so i will recommend not to use own salt, let password_hash() do it for you and set CPU cost minimum 12.

Crunch Much
  • 1,537
  • 1
  • 11
  • 14
  • It says in the PHP documentation that they'll update PASSWORD_DEFAULT to a better algorithm when they make one. Do you think it would be better to just use PASSWORD_DEFAULT forever or maybe instead PASSWORD_BYCRYPT so you can ensure that the algorithm's still the same? – Gumptastic Aug 15 '15 at 04:06
  • Use `PASSWORD_DEFAULT`. Your existing hashes will still be verifiable. – Scott Arciszewski Aug 15 '15 at 04:09
  • @Gumptastic If you dont want to change your hashing algo you can use `PASSWORD_BCRYPT` else If you are using php>=5.5.0 then use `PASSWORD_DEFAULT`. Since `PASSWORD_DEFAULT` will always use stronger hashing algorithm. – Crunch Much Aug 15 '15 at 04:17
  • You're both absolutely right. Thank you. And if I want to check if the passwords need a hash, I can assume it would be a good idea to log them in, use password_needs_rehash() to verify if the password's old and rehash it? And speaking of logins, should I rehash the user's password upon a successful login? – Gumptastic Aug 15 '15 at 04:25
  • @NeelIon `password_needs_rehash()` will only say if a rehash is needed and won't rehash. `boolean password_needs_rehash ( string $hash , string $algo [, string $options ] )` – Charlotte Dunois Aug 15 '15 at 05:40