2017-01-09 11 views
1

Мы являемся платежным шлюзом, похожим на 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

Почему вы проверяете дубликаты против всех записей? Разумеется, добавление требуемого уровня сложности и проверка только фактического пользователя - лучший вариант? – ChrisBint

+0

@ChrisBint Спасибо, но я не понимаю ваш комментарий? Как мы можем гарантировать, что люди не используют один и тот же пароль, не сравнивая свой пароль с теми, которые уже есть в базе данных? –

+3

Почему на самом деле проблема заключается в том, что более одного человека использует один и тот же пароль, если их идентификатор пользователя отличается от другого? ___Who cares___ – RiggsFolly

ответ

-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; 
} 
+0

Серьезно вниз голосов? Есть ли более быстрый способ получить эти данные? Пожалуйста, объясните какой-нибудь орган? –

+0

1. OP, очевидно, использует библиотеку доступа к БД, поэтому преобразование в PDO a) является непрактичным б) он может использовать PDO в любом случае! 2) OP каждый раз получает новую строку, поэтому перемещение ре-хэша вне цикла - это глупость! – RiggsFolly

+0

Ну, но его хэш не имеет ничего общего с данными новой строки. Посмотрите на это: 'function duplicate_passwd_check ($ users_password) {' он использует аргумент '$ users_password', но никакой другой переменной из новой строки. –

-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. Эта проверка также будет считать текущего клиента, который собирается изменить пароль. Пожалуйста, не используйте его, чтобы проверить, является ли старый пароль подлинным или нет.

Как этот код работает

Вам не нужно сравнивать его один за другим. Он тратит время на обработку MySQL и PHP. Вы должны оставить его в MySQL. Мой код получит количество строк. Обычно if должен либо возвращать 0, либо 1. Чтобы сделать его более строгим, я использовал равный или больший.

+0

«Чтобы предотвратить SQL-инъекцию, я поместил на ней mysql_real_escape_string». Да, э-э, http://stackoverflow.com/a/12118602/2224584 –

0

Во-первых, обнаружение повторяющихся паролей почти наверняка является ошибкой. Я советую против этого.

Выработать: Дубликаты пароля создает дополнительный боковой канал. Вот как это использовать.

  1. Попыта зарегистрироваться с использованием имени пользователя и пароля.
  2. Если имя пользователя уже выполнено, создайте список допустимых имен пользователей. (Многие сайты делают это общедоступным, но, возможно, вам это не понадобится.)
  3. Если пароль уже сделан, создайте список допустимых паролей.
  4. Теперь вы можете сопоставить имена пользователей с паролями намного быстрее, чем обычная грубая сила.

Следовательно, единственный выигрышный ход - не играть.

Во-вторых, 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. Ваши пароли перемешаны с использованием Bcrypt, согласно password_hash() умолчанию. Каждый хэш пароля имеет уникальную случайную соль и соответствующий коэффициент затрат (например, 12).
  2. В вашей «двойной проверке пароля» используется bcrypt со статической солью и более высоким коэффициентом стоимости (например, 12 + 1 = 13).

Поскольку контрольная сумма для обнаружения дубликатов предоставляется bcrypt, злоумышленники не получают преимущества здесь, как если бы вы использовали, например, MD5. Однако, поскольку соль не уникальна для пользователя (в противном случае она не функционировала бы так, как предполагалось для обнаружения дубликатов), мы увеличиваем коэффициент затрат.

+0

Если вы используете это для проверки дублированных учетных записей (общий трюк), подумайте только о том, чтобы сделать тот же самый солевой хэш для аккаунтов, которые вы уже подозреваете (например, через ip разделение), чтобы сократить поверхность атаки. – user999305

Смежные вопросы