从数据库中有效地检查散列密码
Efficiently checking a hashed password from database
首先,我已经尽力找到了一个明确的答案。其次,我的代码似乎可以工作,但我想确认我正在以有效的方式执行此操作,并且不会让自己面临安全漏洞。
首先,我在将用户添加到管理员时使用 PHP password_hash table;
$stmt = $dbh->prepare("INSERT INTO admin (username, password) VALUES (:username, :password)");
$stmt->bindParam(':username', $username);
$stmt->bindParam(':password', $password);
$password = password_hash('password', PASSWORD_DEFAULT);
其次,当用户尝试登录时,我仅通过匹配用户名从管理员 table 检索用户,因为我看不到在查询期间检查散列的方法(这是部分我不确定是否有更好的方法),并且还从 POST 输入定义 $password 变量;
$stmt = $dbh->prepare("SELECT * FROM admin WHERE username = :username");
$stmt->bindParam(':username', $username);
$username = $_POST['username'];
// define $password for use in password verify
$password = $_POST['password'];
第三,如果查询有结果,我运行password_verify在用户输入上检查是否匹配,然后根据真假分支。
if ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
if (password_verify($password, $row['password'])) {
session_start();
foreach ($row as $user) {
$_SESSION['user'] = $row['id'];
}
} else {
$errors = true;
}
header('location: leads.php');
}
else {
$errors = true;
}
我知道有很多不同的方法来散列/保护密码,但使用本机 password_hash 函数是我决定采用的方法,我的问题是我是否做得正确/是否有更好的方法?
提前致谢。
基本上,除了已经提到的内容外,您的代码看起来还不错。当您要求改进时,尤其是与性能相关的改进,我们开始:
- 在
username
上添加索引
如果您的 table 有很多条目,请从 username
中删除索引,添加一个名为 hash
的新列和一个索引并重写您的插入select 像这样:
INSERT INTO admin (username, password, hash) VALUES (:username, :password, crc32(username))
SELECT * FROM admin WHERE username = :username AND hash=crc32(:username)
我假设您使用 MySQL,因此将 LIMIT 1
添加到您的查询有助于优化器并在找到该行后停止搜索。
如果只处理一行,也可以避免 foreach
循环。
顺便说一句:header('location: leads.php');
应该读作 header('Location: leads.php');
并且使用绝对路径使事情更健壮。
首先,我已经尽力找到了一个明确的答案。其次,我的代码似乎可以工作,但我想确认我正在以有效的方式执行此操作,并且不会让自己面临安全漏洞。
首先,我在将用户添加到管理员时使用 PHP password_hash table;
$stmt = $dbh->prepare("INSERT INTO admin (username, password) VALUES (:username, :password)");
$stmt->bindParam(':username', $username);
$stmt->bindParam(':password', $password);
$password = password_hash('password', PASSWORD_DEFAULT);
其次,当用户尝试登录时,我仅通过匹配用户名从管理员 table 检索用户,因为我看不到在查询期间检查散列的方法(这是部分我不确定是否有更好的方法),并且还从 POST 输入定义 $password 变量;
$stmt = $dbh->prepare("SELECT * FROM admin WHERE username = :username");
$stmt->bindParam(':username', $username);
$username = $_POST['username'];
// define $password for use in password verify
$password = $_POST['password'];
第三,如果查询有结果,我运行password_verify在用户输入上检查是否匹配,然后根据真假分支。
if ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
if (password_verify($password, $row['password'])) {
session_start();
foreach ($row as $user) {
$_SESSION['user'] = $row['id'];
}
} else {
$errors = true;
}
header('location: leads.php');
}
else {
$errors = true;
}
我知道有很多不同的方法来散列/保护密码,但使用本机 password_hash 函数是我决定采用的方法,我的问题是我是否做得正确/是否有更好的方法?
提前致谢。
基本上,除了已经提到的内容外,您的代码看起来还不错。当您要求改进时,尤其是与性能相关的改进,我们开始:
- 在
username
上添加索引
如果您的 table 有很多条目,请从
username
中删除索引,添加一个名为hash
的新列和一个索引并重写您的插入select 像这样:INSERT INTO admin (username, password, hash) VALUES (:username, :password, crc32(username))
SELECT * FROM admin WHERE username = :username AND hash=crc32(:username)
我假设您使用 MySQL,因此将 LIMIT 1
添加到您的查询有助于优化器并在找到该行后停止搜索。
如果只处理一行,也可以避免 foreach
循环。
顺便说一句:header('location: leads.php');
应该读作 header('Location: leads.php');
并且使用绝对路径使事情更健壮。