优化重复密码检查功能

时间:2017-01-09 08:47:40

标签: php mysql

我们是支付网关,类似于PayPal,每个用户都需要一个帐户,最近我们一直在创建帐户流程中遇到资源问题。

我们已经缩小了我们的重复密码安全功能,调用了数据库,使系统停止运转。 我们已经为下面的函数提供了代码。 user_accounts表中有> 500,000行。我们可以做些什么来优化这个功能?

P.S。我们知道SHA1是一种不常用的存储密码的方式,我们正在考虑升级。

function duplicate_passwd_check($users_password) {
  $pass_array = $sql->fetch("SELECT * FROM user_accounts");

  foreach ($pass_array as $db_key => $db_value) {
    // perform hashing
    $salt = md5($users_password . SITE_PEPPER);
    $hashed_users_password = sha1($users_password . $salt);
    // perform check
    if ($db_value['hashed_password'] == $hashed_users_password) {
      $error->add("Password already in use, please use a different one.");
      locate("register"); // redirect to register page
    }
  }
  return 0;
}

3 个答案:

答案 0 :(得分:0)

首先,检测重复密码几乎肯定是一个错误。我建议不要这样做。

  

详细说明:重复密码检测会创建额外的旁道。以下是如何利用它。

     
      
  1. 尝试使用用户名和密码注册。
  2.   
  3. 如果已使用用户名,请构建有效用户名列表。 (但是,很多网站都公开这个网站,所以你可能不需要它。)
  4.   
  5. 如果已经输入密码,请构建有效密码列表。
  6.   
  7. 您现在可以比传统的蛮力更快地将用户名映射到密码。
  8.         

    因此,唯一的胜利就是不参加比赛。

其次,use password_hash() and password_verify()并确保follow a non-opportunistic strategy for migrating your legacy hashes

如果您仍想检测重复密码,请勿使用快速哈希功能。如果攻击者获取了您的数据库,他们将攻击该数据库,而不是正确的密码哈希值。

相反,请考虑这样的事情:

class FooBarAuth
{
    const DUPE_SALT = '$2a$13$s1OlQvuC1wmgFMEYsGNzA.';

    public function doSignup(string $username, string $password)
    {
        $passwordHash = password_hash($password, PASSWORD_DEFAULT, ['cost' => 12]);
        $dupeHash = crypt($password, static::DUPE_SALT);

        // (Using paragonie/easydb in this example)
        if ($this->db->exists('SELECT count(*) FROM users WHERE dupe_hash = ?', $dupeHash)) {
            throw new Exception('Duplicate password detected');
        }
        // Proceed with the rest of your onboarding logic here
    }
}

一些关键点:

  1. 根据password_hash()默认值,使用bcrypt对您的密码进行哈希处理。每个密码哈希都有一个唯一的随机盐和一个适当的成本因子(例如12)。
  2. 您的“重复密码检查”使用带有静态盐和更高成本系数的bcrypt(例如12 + 1 = 13)。
  3. 由于重复检测校验和是由bcrypt提供的,因此攻击者在这里没有获得优势,就像你使用例如MD5。但是,由于盐不是每用户唯一的(否则它不能用于检测重复项),我们会增加成本因素​​。

答案 1 :(得分:-1)

问题:

为什么要检查所有记录的重复记录?尝试使用查询。

优化代码:

function duplicate_passwd_check($users_password) {
  $salt = md5($users_password . SITE_PEPPER);
  $hashed_users_password = sha1($users_password . $salt);
  $pass_array = $sql->fetch("SELECT COUNT(*) as TotalNumberUsed FROM user_accounts WHERE hashed_password = '".$hashed_users_password."'");
  if((int)$pass_array["TotalNumberUsed"] > 0){
    $error->add("Password already in use, please use a different one.");
    locate("register"); // redirect to register page
    return 1;
  }
  return 0;
}

答案 2 :(得分:-1)

很难想象所有用户必须拥有不同的密码而不是使用旧密码。但如果你想这样做,这应该是最好的方法:

function duplicate_passwd_check($users_password) {
    $salt = md5($users_password . SITE_PEPPER);
    $hashed_users_password = sha1($users_password . $salt);
    $duplicated_row = $sql->result("SELECT COUNT(user_id) FROM user_accounts WHERE hashed_password = '" . mysql_real_escape_string($hashed_users_password . "'");

    // perform check
    if ($duplicated_row >= 1) {
          $error->add("Password already in use, please use a different one.");
          locate("register"); // redirect to register page
    }
}

备注

  1. 我不知道您正在使用哪个SQL类,我假设您使用的是MySQL类。为了防止SQL注入,我在其上添加了mysql_real_escape_string
  2. 我不知道你是否有$sql->result但是这个函数应该输出结果而不是将它作为一个数组排序。
  3. user_id应该是存储用户信息的主键。如果我的命名猜测错误,你应该改变它。
  4. 此检查还将计算要更改密码的当前客户端。请不要用它来检查旧密码是否正确。
  5. 此代码的工作原理

    您无需逐一比较。它浪费了MySQL和PHP的处理时间。你应该把它留给MySQL。我的代码将获得多少行。通常if应该返回0或1.为了使它更严格,我使用等于或大于。