0

I'm having an issue with the code below. It was pulled from here and slightly modified to include validation and fire when the appropriate forms were submitted and pass validation. The issue is with the hashing of the password.

The hashed password does not match the password in the database even though the password itself and the salt are identical. I've checked the $hashed_password variable against what is being written to the database. They match perfectly. On the login side, the salt matches, but when the same password is used, the part after the salt is different? The results look like this:

$2a$05$Bj79bEbmWG9GeMbBAIXID.zMtNecb3B5qWkiGZrSccWcefQG7IXUy $2a$05$Bj79bEbmWG9GeMbBAIXID.6qNLDcZ21XAKoSOIriqTxlAUjjTygoy

There was a problem with your user name or password.

Unless I'm missing something obvious, the only thing I could imagine is a different algorithm being used on register from login, but I'm not sure how to confirm that or correct. Any help is greatly appreciated.

<?php

$password = mysql_real_escape_string($_POST['password']);
$username = mysql_real_escape_string($_POST['username']);

//This string tells crypt to use blowfish for 5 rounds.
$Blowfish_Pre = '$2a$05$';
$Blowfish_End = '$';

// // PHP code you need to register a user
if($_SERVER['REQUEST_METHOD'] == "POST" && isset($_POST['register'])) {
    global $valid;
    user_reg_validate($con, $_POST['username'], $_POST['email'], $_POST['password'], $_POST    ['password2']);
    if ($valid != false) {
        // Blowfish accepts these characters for salts.
        $Allowed_Chars =     'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./';
        $Chars_Len = 63;

        // 18 would be secure as well.
        $Salt_Length = 21;
        $mysql_date = date( 'Y-m-d' );

        $salt = "";
        for($i=0; $i<$Salt_Length; $i++) {
            $salt .= $Allowed_Chars[mt_rand(0,$Chars_Len)];
        }

        $bcrypt_salt = $Blowfish_Pre . $salt . $Blowfish_End;
        $hashed_password = crypt($password, $bcrypt_salt);

        $sql = "INSERT INTO login (username, salt, password) VALUES ('$username', '$salt', '$hashed_password')";
        mysqli_query($con, $sql) or die( mysql_error() );
    }
}

if($_SERVER['REQUEST_METHOD'] == "POST" && isset($_POST['login'])) {
    global $valid;
    user_login_validate($con, $_POST['username'], $_POST['password']);
    if($valid != false) {
        // Now to verify a user’s password
        $sql = "SELECT salt, password FROM login WHERE username='$username'";
        $result = mysqli_query($con, $sql) or die( mysql_error() );
        $row = mysqli_fetch_assoc($result);
        $hashed_pass = crypt($password, $Blowfish_Pre . $row['salt'] . $Blowfish_End);
        echo $hashed_pass . "</br>";
        echo $row['password'] . "</br>";
        if ($hashed_pass == $row['password']) {
            echo 'Password verified!';
        } else {
            echo 'There was a problem with your user name or password.';
        }
    }
}
?>
user2530671
  • 428
  • 3
  • 13
  • 1
    Your salt and method seem correct, can you verify that the passwords match? I would also like to point out that your salt generation isn't cryptographically strong, use `openssl_random_pseudo_bytes` or `mcrypt_create_iv` – Halcyon Aug 20 '15 at 16:51
  • @Halcyon that let me down the right path. I ended up having a mislabeled field in my form and I had to make some changes to the validation function to reflect that as well. It's working. I'm off to study the salt generators you mentioned. Thanks! – user2530671 Aug 20 '15 at 18:39

1 Answers1

2

You can have the same for free, just use the function password_hash(). This function will generate a cryptographically safe salt and make it part of the hash-value, so there is no need for the separate database field. It also uses the $2y signature and adds a sensible default for the cost parameter (5 is very low).

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

// 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);

Also be aware that your code is not protected against SQL-injection. Switch to prepared statements as soon as you can, writing code will become even easier than building the query as you did, and both MYSQLI and PDO are supporting it. This answer could give you a start.

Community
  • 1
  • 1
martinstoeckli
  • 23,430
  • 6
  • 56
  • 87
  • Yes, excellent advice! Unfortunately I'm still back at PHP 5.3 and I don't think it's supported until 5.5. I need to upgrade but I'm not sure if it will run on apache 2.2. All this is on wamp so I should probably just update the whole thing. About the prepared statements, all the examples I see involve oop which I understand, but haven't implemented in a project yet. I'm still learning... – user2530671 Aug 21 '15 at 13:29
  • @user2530671 - No problem, for PHP version 5.3.7 and later, there exists a [compatibility pack](https://github.com/ircmaxell/password_compat/blob/master/lib/password.php), from the same author that made the password_hash() function. For PHP versions before 5.3.7 there is no support for crypt() with 2y, the unicode safe BCrypt algorithm. One could replace it instead with 2a, which is the best alternative for earlier PHP versions. – martinstoeckli Aug 21 '15 at 13:47