14

I modified my old post. I tried the crypt() function and now trying to work with password_hash() and password_verify() to verify the encrypted password coming from database but on each call, password_hash() function retuns a different encrypted string and password_verify() cannot match it.

This is how I am doing this.

 //please ignore the syntax error if any

$data = '11';
$dbpass = password_hash($data, PASSWORD_BCRYPT);
echo $dbpass;  // displays the random strings on each page refresh.

Once password is saved into database does not get match during the login process. Below is my actual function.

   private function process_data($password){
    $password = __STR.$password.__STR;
    return  password_hash($password, PASSWORD_BCRYPT);

  }
  private function processed($login_password, $dbpassword){
    $login_password = __STR.$login_password.__STR;
    return password_verify($login_password, $dbpassword);
  }

On each function call for creating a hashed string for password, the function returns the different string next time.

ircmaxell
  • 163,128
  • 34
  • 264
  • 314
Hunza Ali
  • 189
  • 1
  • 3
  • 10
  • Please read why you shouldn't use MD5 to hash passwords: http://www.php.net/manual/en/faq.passwords.php – Tchoupi Oct 29 '13 at 15:42
  • I believe (and have read in many places) it is better practice to give each password its own salt. – Ethan Oct 29 '13 at 15:42
  • Agreed with you. I have seen the reverse of MD5 encryption is easily available over the internet. But i tried to add a salt in it to make it hard for the hacker to figure out what this password is. Isn't this a good idea? I always look for an expert opinion. – Hunza Ali Oct 29 '13 at 15:49
  • @HunzaAli - Updated my answer according to your new question. – martinstoeckli Oct 31 '13 at 12:53
  • Also see Openwall's [PHP password hashing framework](http://www.openwall.com/phpass/) (PHPass). Its portable and hardened against a number of common attacks on user passwords. – jww Oct 12 '14 at 00:05

2 Answers2

45

Ok, Let's go through this one by one.

First, it's hashing, not encryption. Encryption is two-way, hashing is one way. We want to hash. We never want to encrypt. Yes, terminology matters. Please use the correct terminology.

Next, each call to password_hash is supposed to return a different hash. That's because it's generating a strong random salt. This is how it was designed, and how you really should be using it.

Further, DO NOT do the "pepper" thing of adding __STR before and after the password. You're doing nothing but potentially weakening the users password (which is not good). If you want more information around why that's a bad idea: Read This Answer.

Continuing, I would highly recommend that you do not use crypt directly. It is actually surprisingly easy to screw up and generate extremely weak hashes. This is why the password_* api was designed. crypt is a low level library, you want to use a high level library in your code. For more information on ways to screw up bcrypt, check out my blog: Seven Ways To Screw Up Bcrypt.

The Password API was designed to be a simple, one-stop shop. If it's not working for you check the following things:

  1. Are you using PHP >= 5.5.0? Or are you using PHP >= 5.3.7 with password_compat?

    1. Is your database column wide enough?

      It needs to be at least 60 characters long.

    2. Are you checking that the result of the function is a string, and not bool(false)?

      If there is an internal error, it will return a non-string from password_hash.

    3. Are you getting any errors?

      Have you turned on error_reporting to its maximum setting (I recommend -1 to catch everything) and checked that the code isn't throwing any errors?

    4. Are you sure you are using it correctly?

      function saveUser($username, $password) {
          $hash = password_hash($password, PASSWORD_BCRYPT);
          // save $username and $hash to db
      }
      function login($username, $password) {
          // fetch $hash from db
          return password_verify($password, $hash);
      }
      

      Note that each one should be called only once.

  2. Are you using PHP < 5.3.7 with password_compat? If so, this is your problem. You are using the compatability library on an unsupported version of PHP. You may get it to work (certain RedHat distributions have backported the necessary fixes), but you are using an unsupported version. Please upgrade to a reasonable release.

If all else fails, please try running this code and reporting back the output:

$hash = '$2y$04$usesomesillystringfore7hnbRJHxXVLeakoG8K30oukPsA.ztMG';
$test = crypt("password", $hash);
$pass = $test == $hash;

echo "Test for functionality of compat library: " . ($pass ? "Pass" : "Fail");
echo "\n";

If that returns Fail, you are running an unsupported version of PHP and should upgrade. If it returns pass, than the error is somewhere in your logic (the library is functioning fine).

Community
  • 1
  • 1
ircmaxell
  • 163,128
  • 34
  • 264
  • 314
  • Thank you so much @ircmaxell. I tried your saveUser and login function as it is and it worked. Now i am going to try these in my website and that would be the final test. I do not know why it did not work previously. I tried password_hash and and password_verify functions and i could not get the job done before using them. Hopefully these would work in my website not. Let me test and i will get back to you. Thanks – Hunza Ali Oct 31 '13 at 18:30
  • 1
    It worked man. I tried the functionality that you explained in point 4. God bless you. Cheers!! – Hunza Ali Oct 31 '13 at 20:25
  • 1
    @HunzaAli: Glad to hear it! – ircmaxell Oct 31 '13 at 20:55
  • Hi, thanks for these steps, I have the same problem on an IIS server (it's always worked on my many Apache websites) using PHP 5.5, 5.6 and 5.6.18 and I don't know how to solve this. I followed your steps and it seems that your "$pass" test return to me "Fail". Can you please help me ? – Alex Feb 12 '16 at 14:03
1

The best way to store passwords is to use PHP's function password_hash(). It automatically generates a cryptographically safe salt for each password and includes it in the resulting 60-character string. You won't have to worry about the salt at all!

// Hash a new password for storing in the database.
// The function automatically generates a cryptographically safe salt.
$hashToStoreInDb = password_hash($password, PASSWORD_BCRYPT);

// Check if the hash of the entered login password, matches the stored hash.
// The salt and the cost factor will be extracted from $existingHashFromDb.
$isPasswordCorrect = password_verify($password, $existingHashFromDb);

Your own scheme is very weak, first you are using MD5 which is ways too fast for generating password hashes, then you use a static salt, which defeats the purpose of a salt. Maybe you want to have a look at my tutorial about safely storing passwords.

Edit to answer updated question:

It is not necessary to add the __STR to the password (if you want to add a pepper there are better ways), but your example functions should actually work. The returned value of password_hash() will be different each time because of the random salt. This is correct, the function password_verify() is able to extract this salt for the verification. In your case the database field is probably the problem. Make sure it can hold a 60 character string.

martinstoeckli
  • 23,430
  • 6
  • 56
  • 87
  • Thanks for your help @martinstoeckli. I figured out before you edited the post. Actually i was confused about getting the random hash string time each time. And i did not have the idea of how the will be verified. But i figured out, `password_verify()` function takes care of this. Thanks for your help. Cheers!! – Hunza Ali Oct 31 '13 at 22:34