Автор
Dave
Всем тем, кто внедрялся в систему построения хеширования паролей в Vbulletin 4 известно, что форум использует очень простое хеширование паролей, которое можно легко взломать.
Code:
md5(md5($password) . $salt)
Я надеюсь вы понимаете, что это может быть взломано за несколько дней или даже минут при помощи различных словарей, таблиц, расшифровщиков.
Но все это к счастью, мы можем исправить, используя другой метод хеширования. Я изменю систему хеширования паролей Vbulletin используя BCrypt. Этот алгоритм работает более эффективно.
Как будет выглядеть хеширование нашего пароля когда мы применим данный алгоритм я сейчас покажу:
Code:
password_hash(md5(md5($password) . $salt), PASSWORD_BCRYPT, array('salt' => $salt))
Чтобы добиться этого нам нужно выполнить следующие условия:
— Иметь доступ к FTP или иметь разрешение на редактирование .php файлов на сервере.
— PHP версию 5.5+ (так как будет использоваться функция password_hash).
— Необходимо обновить все пароли в текущей базе данных.
— Vbulletin 4.2.2 (эта модификация была сделана именно на этой версии).
— Доступ к базе данных посредством SSH/PHPMyadmin или любой другой клиент.
Обязательно создайте резервную копию файлов, которые мы будем редактировать, для того, чтобы вы всегда могли откатиться назад.
Шаг 1. Изменить тип столбца password.
В первую очередь мы должны изменить тип столбца password в таблице user. Сделать это можно либо через PHPMyAdmin путем изменения его типа char(60) или выполнив следующий запрос:
Code:
ALTER TABLE user MODIFY password char(60);
Шаг 2. Редактирование /includes/functions_login.php
Далее изменим функцию verify_authentication в файле /includes/functions_login.php
Ишем:
Code:
if (
$vbulletin->userinfo['password'] != iif($password AND !$md5password, md5(md5($password) . $vbulletin->userinfo['salt']), '') AND
$vbulletin->userinfo['password'] != iif($md5password, md5($md5password . $vbulletin->userinfo['salt']), '') AND
$vbulletin->userinfo['password'] != iif($md5password_utf, md5($md5password_utf . $vbulletin->userinfo['salt']), '')
)
И заменяем на:
Code:
$hash1 = password_hash(md5(md5($password) . $vbulletin->userinfo['salt']), PASSWORD_BCRYPT, array('salt' => $vbulletin->userinfo['salt']));
$hash2 = password_hash(md5($md5password . $vbulletin->userinfo['salt']), PASSWORD_BCRYPT, array('salt' => $vbulletin->userinfo['salt']));
$hash3 = password_hash(md5($md5password_utf . $vbulletin->userinfo['salt']), PASSWORD_BCRYPT, array('salt' => $vbulletin->userinfo['salt']));
if (
$vbulletin->userinfo['password'] != iif($password AND !$md5password, $hash1, '') AND
$vbulletin->userinfo['password'] != iif($md5password, $hash2, '') AND
$vbulletin->userinfo['password'] != iif($md5password_utf, $hash3, '')
)
Шаг 3. Редактировать /includes/class_dm_user.php
Этот файл содержит функции verify_password и hash_password , которые должны быть изменены.
Ищем функцию hash_password:
Code:
function hash_password($password, $salt)
{
// if the password is not already an md5, md5 it now
if ($password == '')
{
}
else if (!$this->verify_md5($password))
{
$password = md5($password);
}
// hash the md5'd password with the salt
return md5($password . $salt);
}
И заменяем ее на:
Code:
function hash_password($password, $salt)
{
// if the password is not already an md5, md5 it now
if ($password == '')
{
}
else if (!$this->verify_md5($password))
{
$password = md5($password);
}
// hash the md5'd password with the salt
return password_hash(md5($password . $salt), PASSWORD_BCRYPT, array('salt' => $salt));
}
Ищем функцию verify_password:
Code:
function verify_password(&$password)
{
//regenerate the salt when the password is changed. No reason not to and its
//an easy way to increase the size when the user changes their password (doing
//it this way avoids having to reset all of the passwords)
$this->user['salt'] = $salt = $this->fetch_user_salt();
// generate the password
$password = $this->hash_password($password, $salt);
if (!defined('ALLOW_SAME_USERNAME_PASSWORD'))
{
// check if password is same as username; if so, set an error and return false
if ($password == md5(md5($this->fetch_field('username')) . $salt))
{
$this->error('sameusernamepass');
return false;
}
}
$this->set('passworddate', 'FROM_UNIXTIME(' . TIMENOW . ')', false);
return true;
}
и заменяем ее на:
Code:
function verify_password(&$password)
{
//regenerate the salt when the password is changed. No reason not to and its
//an easy way to increase the size when the user changes their password (doing
//it this way avoids having to reset all of the passwords)
$this->user['salt'] = $salt = $this->fetch_user_salt();
// generate the password
$password = $this->hash_password($password, $salt);
if (!defined('ALLOW_SAME_USERNAME_PASSWORD'))
{
// check if password is same as username; if so, set an error and return false
if ($password == password_hash(md5(md5($this->fetch_field('username')) . $salt), PASSWORD_BCRYPT, array('salt' => $salt)))
{
$this->error('sameusernamepass');
return false;
}
}
$this->set('passworddate', 'FROM_UNIXTIME(' . TIMENOW . ')', false);
return true;
}
Для чего мы изменяем эту функцию? Для того чтобы проверить, что пароль не совпадает с именем пользователя, что не разрешается по умолчанию.
Шаг 4. Обновить все пароли в таблице user.
Единственным недостатком всего этого, является то, что мы должны обновить все пароли в базе данных. Но не стоит беспокоится, некому не нужно будет менять свои пароли, чтобы зашифровать все по новому, поскольку будет использован старый пароль, но хеширование будет происходить посредством BCrypt.
Пожалуйста, прежде чем это делать, создайте резервную копию базы данных или таблицы user, для того, что если, что-то пойдет не так, вы всегда можете откатиться.
Создайте в корне вашего форума файл с именем update_passwords.php, в который вставьте следующее:
Code:
require("./global.php");
$query = $db->query_read("SELECT userid, password, salt FROM user WHERE password NOT LIKE '$%' ORDER BY userid ASC");
echo 'We have ' . $db->num_rows($query) . ' password(s) to update..<br><br>';
while($row = $db->fetch_array($query)){
$db->query_write("UPDATE user SET password = '" . password_hash($row['password'], PASSWORD_BCRYPT, array('salt' => $row['salt'])) . "' WHERE userid = '" . $row['userid'] . "'");
}
echo 'Updated all passwords, re-run this script to be sure!';
Это позводит вам обновить все текущие хеши паролей используя BCrypt. В случае если время ожидания сценария истекло, просто запустите скрипт еще раз изменив script execution timeout.
Шаг 5. Проверка функциональности.
Сейчас зайдите на форум и посмотрите работает ли все

В случае если вы получаете сообщение что ввели неправельный логин или пароль, значит вы что-то сделали не так.
P/S человек давший тест форум пожелал оказаться неизвестным, в любом случае спасибо за проверку, все работает.