更改密码脚本有效但不会将正确的密码写入数据库

Change password script works but doesn't write correct password into database

我有这个更改密码的脚本:

<?php
/*
Page/Script Created by Shawn Holderfield
*/

//Establish output variable - For displaying Error Messages
$msg = "";

//Check to see if the form has been submitted
if (mysql_real_escape_string($_POST['submit'])):

    //Establish Post form variables
    $username = mysql_real_escape_string($_POST['username']);
    $password = mysql_real_escape_string(md5($_POST['password']));
    $npassword = mysql_real_escape_string(md5($_POST['npassword']));
    $rpassword = mysql_real_escape_string(md5($_POST['rpassword']));

    //Connect to the Database Server
    mysql_connect("mysql..", "", "")or die(mysql_error());

    // Connect to the database
    mysql_select_db("") or die(mysql_error());

    // Query the database - To find which user we're working with
    $sql = "SELECT * FROM members WHERE username = '$username' ";
    $query = mysql_query($sql);
    $numrows = mysql_num_rows($query);
    //Gather database information
    while ($rows = mysql_fetch_array($query)):

        $username == $rows['username'];
        $password == $rows['password'];


    endwhile;

    //Validate The Form
    if (empty($username) || empty($password) || empty($npassword) || empty($rpassword)):

        $msg = "All fields are required";

    elseif ($numrows == 0):

        $msg = "This username does not exist";

    elseif ($password != $password):

        $msg = "The CURRENT password you entered is incorrect.";

    elseif ($npassword != $rpassword):

        $msg = "Your new passwords do not match";

    elseif ($npassword == $password):

        $msg = "Your new password cannot match your old password";

    else:

        //$msg = "Your Password has been changed.";
        mysql_query("UPDATE members SET password = '$npassword' WHERE username = '$username'");



        endif;
endif;

?>
<html>
    <head>
        <title>Change Password</title>
    </head>
    <body>
            <form method="POST" action="">
                <table border="0">
                    <tr>
                        <td align="right">Username: </td>
                        <td><input type="TEXT" name="username" value=""/></td>
                    </tr>
                    <tr>
                        <td align="right">Current Password: </td>
                        <td><input type="password" name="password" value=""/></td>
                    </tr>
                    <tr>
                        <td align="right">New Password: </td>
                        <td><input type="password" name="npassword" value=""/></td>
                    </tr>
                    <tr>
                        <td align="right">Repeat New Password: </td>
                        <td><input type="password" name="rpassword" value=""/></td>
                    </tr>
                    <tr><td>
                        <input type="submit" name="submit" value="Change Password"/>
                        </td>
                    </tr>
                </table>
            </form>
        <br>
<?php echo $msg; ?>
    </body>
</html>

这实际上工作正常,访问数据库并覆盖当前密码,但它不会像注册时那样散列密码 注册过程脚本:

<?php
include_once 'db_connect.php';
include_once 'psl-config.php';

$error_msg = "";

if (isset($_POST['username'], $_POST['email'], $_POST['p'])) {
    // Sanitize and validate the data passed in
    $username = filter_input(INPUT_POST, 'username', FILTER_SANITIZE_STRING);
    $email = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL);
    $email = filter_var($email, FILTER_VALIDATE_EMAIL);
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        // Not a valid email
        $error_msg .= '<p class="error">The email address you entered is not valid</p>';
    }

    $password = filter_input(INPUT_POST, 'p', FILTER_SANITIZE_STRING);
    if (strlen($password) != 128) {
        // The hashed pwd should be 128 characters long.
        // If it's not, something really odd has happened
        $error_msg .= '<p class="error">Invalid password configuration.</p>';
    }

    // Username validity and password validity have been checked client side.
    // This should should be adequate as nobody gains any advantage from
    // breaking these rules.
    //

    $prep_stmt = "SELECT id FROM members WHERE email = ? LIMIT 1";
    $stmt = $mysqli->prepare($prep_stmt);

   // check existing email  
    if ($stmt) {
        $stmt->bind_param('s', $email);
        $stmt->execute();
        $stmt->store_result();

        if ($stmt->num_rows == 1) {
            // A user with this email address already exists
            $error_msg .= '<p class="error">A user with this email address already exists.</p>';
                        $stmt->close();
        }
                $stmt->close();
    } else {
        $error_msg .= '<p class="error">Database error Line 39</p>';
                $stmt->close();
    }

    // check existing username
    $prep_stmt = "SELECT id FROM members WHERE username = ? LIMIT 1";
    $stmt = $mysqli->prepare($prep_stmt);

    if ($stmt) {
        $stmt->bind_param('s', $username);
        $stmt->execute();
        $stmt->store_result();

                if ($stmt->num_rows == 1) {
                        // A user with this username already exists
                        $error_msg .= '<p class="error">A user with this username already exists</p>';
                        $stmt->close();
                }
                $stmt->close();
        } else {
                $error_msg .= '<p class="error">Database error line 55</p>';
                $stmt->close();
        }

    // TODO: 
    // We'll also have to account for the situation where the user doesn't have
    // rights to do registration, by checking what type of user is attempting to
    // perform the operation.

    if (empty($error_msg)) {
        // Create a random salt
        //$random_salt = hash('sha512', uniqid(openssl_random_pseudo_bytes(16), TRUE)); // Did not work
        $random_salt = hash('sha512', uniqid(mt_rand(1, mt_getrandmax()), true));

        // Create salted password 
        $password = hash('sha512', $password . $random_salt);

        // Insert the new user into the database 
        if ($insert_stmt = $mysqli->prepare("INSERT INTO members (username, email, password, salt) VALUES (?, ?, ?, ?)")) {
            $insert_stmt->bind_param('ssss', $username, $email, $password, $random_salt);
            // Execute the prepared query.
            if (! $insert_stmt->execute()) {
                header('Location: ../error.php?err=Registration failure: INSERT');
            }
        }
        header('Location: ./continue.php');
    }
}
?>

我该怎么做才能解决这个问题?我希望更改为注册时的格式的密码。因为现在,当我更改密码时,它是散列的而不是加盐的,所以在使用新密码登录时它不起作用。

编辑: 这是登录脚本:

<?php
include_once 'psl-config.php';

function sec_session_start() {
    $session_name = 'sec_session_id';   // Set a custom session name
    $secure = SECURE;
    // This stops JavaScript being able to access the session id.
    $httponly = true;
    // Forces sessions to only use cookies.
    if (ini_set('session.use_only_cookies', 1) === FALSE) {
        header("Location: ../error.php?err=Could not initiate a safe session (ini_set)");
        exit();
    }
    // Gets current cookies params.
    $cookieParams = session_get_cookie_params();
    session_set_cookie_params($cookieParams["lifetime"],
        $cookieParams["path"], 
        $cookieParams["domain"], 
        $secure,
        $httponly);
    // Sets the session name to the one set above.
    session_name($session_name);
    session_start();            // Start the PHP session 
    session_regenerate_id(true);    // regenerated the session, delete the old one. 
}
function login($email, $password, $mysqli) {
    // Using prepared statements means that SQL injection is not possible. 
    if ($stmt = $mysqli->prepare("SELECT id, username, password 
        FROM members
       WHERE email = ?
        LIMIT 1")) {
        $stmt->bind_param('s', $email);  // Bind "$email" to parameter.
        $stmt->execute();    // Execute the prepared query.
        $stmt->store_result();

        // get variables from result.
        $stmt->bind_result($user_id, $username, $db_password );
        $stmt->fetch();

        // hash the password
        $passwordHash = password_hash($password, PASSWORD_BCRYPT);
        if ($stmt->num_rows == 1) {
            // If the user exists we check if the account is locked
            // from too many login attempts 

            if (checkbrute($user_id, $mysqli) == true) {
                // Account is locked 
                // Send an email to user saying their account is locked
                return false;
            } else {
                // Check if the password in the database matches
                // the password the user submitted.
                if ($db_password == $password) {
                    // Password is correct!
                    // Get the user-agent string of the user.
                    $user_browser = $_SERVER['HTTP_USER_AGENT'];
                    // XSS protection as we might print this value
                    $user_id = preg_replace("/[^0-9]+/", "", $user_id);
                    $_SESSION['user_id'] = $user_id;
                    // XSS protection as we might print this value
                    $username = preg_replace("/[^a-zA-Z0-9_\-]+/", 
                                                                "", 
                                                                $username);
                    $_SESSION['username'] = $username;
            $_SESSION['email'] = $email;
                    $_SESSION['login_string'] = hash('sha512', 
                              $password . $user_browser);
                    // Login successful.
                    return true;
                } else {
                    // Password is not correct
                    // We record this attempt in the database
                    $now = time();
                    $mysqli->query("INSERT INTO login_attempts(user_id, time)
                                    VALUES ('$user_id', '$now')");
                    return false;
                }
            }
        } else {
            // No user exists.
            return false;
        }
    }
}
function checkbrute($user_id, $mysqli) {
    // Get timestamp of current time 
    $now = time();

    // All login attempts are counted from the past 2 hours. 
    $valid_attempts = $now - (2 * 60 * 60);

    if ($stmt = $mysqli->prepare("SELECT time 
                             FROM login_attempts 
                             WHERE user_id = ? 
                            AND time > '$valid_attempts'")) {
        $stmt->bind_param('i', $user_id);

        // Execute the prepared query. 
        $stmt->execute();
        $stmt->store_result();

        // If there have been more than 5 failed logins 
        if ($stmt->num_rows > 5) {
            return true;
        } else {
            return false;
        }
    }
}
function login_check($mysqli) {
    // Check if all session variables are set 
    if (isset($_SESSION['user_id'], 
            $_SESSION['email'],
                        $_SESSION['username'], 
                        $_SESSION['login_string'])) {

        $user_id = $_SESSION['user_id'];
    $email = $_SESSION['email'];
        $login_string = $_SESSION['login_string'];
        $username = $_SESSION['username'];

        // Get the user-agent string of the user.
        $user_browser = $_SERVER['HTTP_USER_AGENT'];

        if ($stmt = $mysqli->prepare("SELECT password 
                                      FROM members 
                                      WHERE id = ? LIMIT 1")) {
            // Bind "$user_id" to parameter. 
            $stmt->bind_param('i', $user_id);
            $stmt->execute();   // Execute the prepared query.
            $stmt->store_result();

            if ($stmt->num_rows == 1) {
                // If the user exists get variables from result.
                $stmt->bind_result($password);
                $stmt->fetch();
                $login_check = hash('sha512', $password . $user_browser);

                if ($login_check == $login_string) {
                    // Logged In!!!! 
                    return true;
                } else {
                    // Not logged in 
                    return false;
                }
            } else {
                // Not logged in 
                return false;
            }
        } else {
            // Not logged in 
            return false;
        }
    } else {
        // Not logged in 
        return false;
    }
}
function esc_url($url) {

    if ('' == $url) {
        return $url;
    }

    $url = preg_replace('|[^a-z0-9-~+_.?#=!&;,/:%@$\|*\'()\x80-\xff]|i', '', $url);

    $strip = array('%0d', '%0a', '%0D', '%0A');
    $url = (string) $url;

    $count = 1;
    while ($count) {
        $url = str_replace($strip, '', $url, $count);
    }

    $url = str_replace(';//', '://', $url);

    $url = htmlentities($url);

    $url = str_replace('&amp;', '&#038;', $url);
    $url = str_replace("'", '&#039;', $url);

    if ($url[0] !== '/') {
        // We're only interested in relative links from $_SERVER['PHP_SELF']
        return '';
    } else {
        return $url;
    }
}

修改后的代码中多了一些注释。这是您应该如何做的。 从您的数据库中获取旧盐

//Gather database information
while ($rows = mysql_fetch_array($query)):

    $username = $rows['username']; //single equals to assign value
    $password = $rows['password']; 
    $user_salt = $rows['salt']; // here get old salt

endwhile; 

这部分阅读和使用旧盐

else:

    //$msg = "Your Password has been changed."; 
  $salted_password = hash('sha512', $npassword . $user_salt);// here use old salt  
 mysql_query("UPDATE members SET password = '$salted_password' WHERE username = '$username'");


endif;

请查看 password_hash() and the password_verify() 函数,MD5 或 sha512 适合散列密码,因为它们速度太快并且可能被暴力破解太容易了。 password_hash 函数将自行生成盐,您不需要在数据库中额外的字段来存储它。

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

注意:使用password_hash功能,不需要用mysql_real_escape_string对密码进行转义,直接使用原来的用户输入即可。

编辑:

所以我会尝试指出您的代码中的一些问题,老实说它看起来有点奇怪。我会尝试重新开始并使用 PDO 或 mysqli 而不是 mysql 函数,这将更容易防止 SQL-injection.

更改密码脚本

一个问题是您重用了 $password 变量,这导致了错误的比较,尽管因为您使用了 == 而不是 = 它什么都不做:

$password = mysql_real_escape_string(md5($_POST['password']));
...
$password == $rows['password'];
...
elseif ($password != $password):
  $msg = "The CURRENT password you entered is incorrect.";

我自己花了很多时间来寻找描述性的变量名,这有助于防止此类错误。

$oldPassword = $_POST['password'];
...
$passwordHashFromDb = $rows['password'];
...
elseif (!password_verify($oldPassword, $passwordHashFromDb))
  $msg = "The CURRENT password you entered is incorrect.";

然后在将新密码存储到数据库之前计算哈希:

$username = mysql_real_escape_string($_POST['username']);
$newPassword = $_POST['npassword'];
...
$newPasswordHash = password_hash($newPassword, PASSWORD_BCRYPT);
mysql_query("UPDATE members SET password = '$newPasswordHash' WHERE username = '$username'");

注册脚本

在您的注册脚本中还有其他问题,您肯定不希望用户输入 128 个字符的密码吗?

if (strlen($password) != 128) { // looks strange to me

您应该使用与上面相同的算法,而不是使用不同的哈希算法:

// Create salted password 
$passwordHash = password_hash($password, PASSWORD_BCRYPT);

// Insert the new user into the database 
if ($insert_stmt = $mysqli->prepare("INSERT INTO members (username, email, password) VALUES (?, ?, ?)")) {
  $insert_stmt->bind_param('sss', $username, $email, $passwordHash);
...

登录脚本

在您的登录脚本中检查密码是否与数据库中存储的密码匹配。

if ($db_password == $password) {
  // Password is correct!

你应该像这样测试散列:

if (password_verify($password, $db_password) {
  // Password is correct!

对 password_hash() 函数的调用对登录脚本没有帮助,应将其删除。