如果我有这个用于登录系统的PHP脚本:
$user = $_POST['user_name'];
$pass = md5($_POST['user_pass']);
require_once("connection_file.php");
$sql = "SELECT * FROM login_table WHERE user_n = :us AND user_p = :password";
$stmt = $conn->prepare($sql);
$stmt->bindValue(':us', $user, PDO::PARAM_STR);
$stmt->bindValue(':password', $pass, PDO::PARAM_STR);
$stmt->execute();
$result = $stmt->fetchAll();
if($result)
{
//echo $msg = "user exist";
if(session_status() == PHP_SESSION_NONE)
{
session_start();
foreach($result as $row)
{
$hash = password_hash($row['user_pass'], PASSWORD_BCRYPT);
if(password_verify($row['user_pass'], $hash))
{
$_SESSION['userid'] = $row['user_id'];
$_SESSION['role'] = $row['user_role'];
header("Location: homepage.php");
}
}
}
}
else
{
$msg = "Wrong credentials";
header("Location: login_page.php");
}
如您所见,我已经将密码保存在数据库中MD5
并且我正在使用$pass = md5($_POST['user_pass']);
来验证用户输入的文本是否等于MD5
哈希。
现在我的问题是我应该像在这个脚本中使用
password_hash
和password_verify
吗?或者使用MD5
就足够了?我的第二个问题是我可以使用哈希字符串结果将密码保存在数据库中,还是可以使用 md5 结果?
是的,您应该迁移到新的 API,并且永远不要立即将 MD5 用于此目的。
如果您不使用password_hash()
/password_verify()
并希望将代码迁移到更安全的方法,请无缝:
- 向用户帐户表格中添加一个名为"
legacy_password
"(或等效项)的列。 - 计算现有 MD5 哈希的 bcrypt 哈希并将它们存储在数据库中(将
legacy_password
设置为TRUE
)。 - 修改身份验证代码以处理旧标志。
当用户尝试登录时,首先检查是否设置了legacy_password
标志。如果是,请首先使用 MD5 预哈希其密码,然后使用此预哈希值代替其密码。之后,重新计算 bcrypt 哈希并将新哈希存储在数据库中,在此过程中禁用 legacy_password
标志。下面是 PHP 7+ 中一个非常松散的示例:
/**
* This is example code. Please feel free to use it for reference but don't just copy/paste it.
*
* @param string $username Unsafe user-supplied data: The username
* @param string $password Unsafe user-supplied data: The password
* @return int The primary key for that user account
* @throws InvalidUserCredentialsException
*/
public function authenticate(string $username, string $password): int
{
// Database lookup
$stmt = $this->db->prepare("SELECT userid, passwordhash, legacy_password FROM user_accounts WHERE username = ?");
$stmt->execute([$username]);
$stored = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$stored) {
// No such user, throw an exception
throw new InvalidUserCredentialsException();
}
if ($stored['legacy_password']) {
// This is the legacy password upgrade code
if (password_verify(md5($password), $stored['passwordhash'])) {
$newhash = password_hash($password, PASSWORD_DEFAULT);
$stmt = $this->db->prepare("UPDATE user_accounts SET passwordhash = ?, legacy_password = FALSE WHERE userid = ?");
$stmt->execute([$newhash, $stored['userid']]);
// Return the user ID (integer)
return $stored['userid'];
}
} elseif (password_verify($password, $stored['passwordhash'])) {
// This is the general purpose upgrade code e.g. if a future version of PHP upgrades to Argon2
if (password_needs_rehash($stored['passwordhash'], PASSWORD_DEFAULT)) {
$newhash = password_hash($password, PASSWORD_DEFAULT);
$stmt = $this->db->prepare("UPDATE user_accounts SET passwordhash = ? WHERE userid = ?");
$stmt->execute([$newhash, $stored['userid']]);
}
// Return the user ID (integer)
return $stored['userid'];
}
// When all else fails, throw an exception
throw new InvalidUserCredentialsException();
}
用法:
try {
$userid = $this->authenticate($username, $password);
// Update the session state
// Redirect to the post-authentication landing page
} catch (InvalidUserCredentialsException $e) {
// Log the failure
// Redirect to the login form
}
主动升级旧哈希是相对于机会主义策略(在用户登录时重新哈希,但将不安全的哈希保留在数据库中供非活动用户使用)的安全胜利:使用主动策略,如果您的服务器在每个人再次登录之前遭到入侵,则他们的密码已经在使用可接受的算法(示例代码中的 bcrypt)。
上面的示例代码也有 Bcrypt-SHA-384 风格。
此外,这与加密无关。