Хеширование паролей - как обновить?
Там много обсуждение по лучшему алгоритму - но что, если вы уже в производстве? Как вы обновляетесь без использования reset для пользователя?
ИЗМЕНИТЬ/ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Несмотря на то, что я изначально хотел получить решение "быстро исправить" и выбрал orip response, я должен признать, что если безопасность в вашем приложении достаточно важна, чтобы даже беспокоиться об этой проблеме, то быстрое исправление - это неправильный менталитет и его предлагаемое решение, вероятно, неадекватно.
Ответы
Ответ 1
Один из вариантов заключается в том, чтобы ваш сохраненный хэш включал номер версии алгоритма, поэтому вы начинаете с алгоритма 0 (например, MD5) и сохраняете
0:ab0123fe
то при обновлении до SHA-1 вы набросаете номер версии на 1:
1:babababa192df1312
(нет, я знаю, что эти длины, вероятно, неверны).
Таким образом, вы всегда можете указать, какую версию проверять при проверке пароля. Вы можете аннулировать старые алгоритмы, просто очистив хранимые хэши, которые начинаются с этого номера версии.
Если у вас уже есть хеши в производстве без номера версии, просто выберите схему, чтобы вы могли легко распознать неверсированные хэши - например, используя приведенную выше схему двоеточия, любой хэш, который не содержит двоеточие должен по определению предшествовать схеме управления версиями, поэтому можно сделать вывод, что он имеет версию 0 (или что-то еще).
Ответ 2
Отличный способ защитить все существующие пароли: используйте существующий хеш в качестве ввода нового и лучшего хеша пароля.
Итак, если ваши существующие хэши являются прямыми MD5, и вы планируете перейти в какую-то форму PBKDF2 (или bcrypt или scrypt), затем измените хэш пароля на
PBKDF2( MD5( password ) )
У вас уже есть MD5 в вашей базе данных, поэтому все, что вы делаете, это применить к нему PBKDF2.
Причина, по которой это хорошо работает, заключается в том, что слабые стороны MD5 и других хэшей (например, SHA- *) не влияют на использование пароля. Например, его уязвимости в столкновении являются разрушительными для цифровых подписей, но они не влияют на хэши паролей. По сравнению с более длинными хэшами MD5 несколько уменьшает пространство для хэш-поиска с его 128-битным выходом, но это незначительно по сравнению с самим местом поиска паролей, которое намного меньше.
Что делает сильный хеш пароля медленным (достигнутый в PBKDF2 итерациями) и случайной, достаточно большой солью - исходный MD5 не оказывает неблагоприятного воздействия на любой из них.
И пока вы на нем, добавьте также поле версии для паролей.
EDIT. Криптография StackExchange имеет интересную дискуссию по этому методу.
Ответ 3
Подождите, пока ваш пользователь войдет в систему (так что у вас есть пароль в виде открытого текста), затем добавьте его в новый алгоритм и сохраните его в своей базе данных.
Ответ 4
Один из способов сделать это:
- Введите новое поле для нового пароля
- Когда пользователь регистрируется при проверке пароля на старый хэш
- Если все в порядке, введите хэш в текстовый пароль с новым хешем
- Удалить старый хэш
Затем постепенно у вас будут только пароли с новым хэшем
Ответ 5
Теперь вы, вероятно, не можете изменить схему хэширования паролей, если вы не храните пароли в виде обычного текста. Что вы можете сделать, так это повторить хэширование паролей участников с использованием лучшей схемы хэширования после успешного входа в систему.
Вы можете попробовать следующее:
Сначала добавьте новый столбец в таблицу ваших членов или в какой-либо таблице хранятся пароли.
ALTER TABLE members ADD is_pass_upgraded tinyint(1) default 0;
Далее, в вашем коде, который проверяет подлинность пользователей, добавьте дополнительную логику (я использую PHP):
<?php
$username = $_POST['username'];
$password = $_POST['password'];
$auth_success = authenticateUser($username, $password);
if (!$auth_success) {
/**
* They entered the wrong username/password. Redirect them back
* to the login page.
*/
} else {
/**
* Check to see if the member password has been upgraded yet
*/
$username = mysql_real_escape_string($username);
$sql = "SELECT id FROM members WHERE username = '$username' AND is_pass_upgraded = 0 LIMIT 1";
$results = mysql_query($sql);
/**
* Getting any results from the query means their password hasn't been
* upgraded yet. We will upgrade it now.
*/
if (mysql_num_rows($results) > 0) {
/**
* Generate a new password hash using your new algorithm. That's
* what the generateNewPasswordHash() function does.
*/
$password = generateNewPasswordHash($password);
$password = mysql_real_escape_string($password);
/**
* Now that we have a new password hash, we'll update the member table
* with the new password hash, and change the is_pass_upgraded flag.
*/
$sql = "UPDATE members SET password = '$password', is_pass_upgraded = 1 WHERE username = '$username' LIMIT 1";
mysql_query($sql);
}
}
Ваша функция authenticateUser() должна быть изменена на нечто похожее на это:
<?php
function authenticateUser($username, $password)
{
$username = mysql_real_escape_string($username);
/**
* We need password hashes using your old system (md5 for example)
* and your new system.
*/
$old_password_hashed = md5($password);
$new_password_hashed = generateBetterPasswordHash($password);
$old_password_hashed = mysql_real_escape_string($old_password_hashed);
$new_password_hashed = mysql_real_escape_string($new_password_hashed);
$sql = "SELECT *
FROM members
WHERE username = '$username'
AND
(
(is_pass_upgraded = 0 AND password = '$old_password_hashed')
OR
(is_pass_upgraded = 1 AND password = '$new_password_hashed')
)
LIMIT 1";
$results = mysql_query($sql);
if (mysql_num_rows($results) > 0) {
$row = mysql_fetch_assoc($results);
startUserSession($row);
return true;
} else {
return false;
}
}
Там есть и минусы этого подхода. В верхней части страницы пользовательский пароль становится более безопасным после входа в систему. Недостатком является то, что все пароли не защищены.
Я бы сделал это всего лишь 2 недели. Я пришлю письмо всем своим членам и скажу им, что у них есть 2 недели для входа в свою учетную запись из-за обновления сайта. Если они не смогут войти в систему в течение 2 недель, они должны будут использовать систему восстановления пароля для reset своего пароля.
Ответ 6
Просто повторите хэширование обычного текста, когда они будут аутентифицированы в следующий раз. Oah и использовать SHA-256 с солью base256 (полный байт) и размером 256 байт.