У меня есть этот скрипт для смены пароля:
<?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('&', '&', $url);
$url = str_replace("'", ''', $url);
if ($url[0] !== '/') {
// We're only interested in relative links from $_SERVER['PHP_SELF']
return '';
} else {
return $url;
}
}
Пожалуйста, посмотрите на password_hash () и 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-инъекций.
Одна проблема состоит в том, что вы повторно используете переменную $ 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 () не помогает в сценарии входа и должен быть удален.
Есть еще несколько комментариев в модифицированном коде. Вот как вы должны это сделать.
Получить старую соль из вашей базы данных
//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;