我们是支付网关,类似于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;
}
答案 0 :(得分:0)
首先,检测重复密码几乎肯定是一个错误。我建议不要这样做。
详细说明:重复密码检测会创建额外的旁道。以下是如何利用它。
- 尝试使用用户名和密码注册。
- 如果已使用用户名,请构建有效用户名列表。 (但是,很多网站都公开这个网站,所以你可能不需要它。)
- 如果已经输入密码,请构建有效密码列表。
- 您现在可以比传统的蛮力更快地将用户名映射到密码。
醇>因此,唯一的胜利就是不参加比赛。
其次,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
}
}
一些关键点:
password_hash()
默认值,使用bcrypt对您的密码进行哈希处理。每个密码哈希都有一个唯一的随机盐和一个适当的成本因子(例如12)。由于重复检测校验和是由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
}
}
备注强>
mysql_real_escape_string
。$sql->result
但是这个函数应该输出结果而不是将它作为一个数组排序。user_id
应该是存储用户信息的主键。如果我的命名猜测错误,你应该改变它。此代码的工作原理
您无需逐一比较。它浪费了MySQL和PHP的处理时间。你应该把它留给MySQL。我的代码将获得多少行。通常if
应该返回0或1.为了使它更严格,我使用等于或大于。