从数据库中有效地检查散列密码

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'); 并且使用绝对路径使事情更健壮。