Their have been no tutorials on this for some reason, but today is the day. Mybb uses MD5 for storing passwords and said they are not going to move to Bcrypt until 2.0. I don't think it's a good decision, if someone gets access you your database, they can easily download a decrypted MD5 list (or do it online) to potentially get passwords. Anyway, once you have finished the tutorial, everything is automatic, your users passwords will be converted on login. If they're already logged in and change passwords from the UserCP, the passwords will also be converted. This may work for older & newer versions of mybb, but you will have to pick through the code (and not go by line numbers).
Note
1. You will have to make core edits
2. You need a minimum of PHP 5.5.0
Before you go any further!
MAKE A BACKUP OF YOUR DATABASE AND THE FILES YOU EDIT!
It's better to be safe than sorry.
Edit: \usercp.php
Line: 1203
Select the following:
Replace with:
Edit: \inc\functions_user.php
Line: 73 - 189
Select the following:
Replace with:
Edit: \inc\datahandlers\user.php
Line: 222 - 223
Find:
Replace with:
Once you have done everything above, you can calculate the cost for Bcrypt (Encryption time). This all depends on your server hardware.
Open a new text document, Copy & Paste the below into it, save as cost.php, upload to your server and access it in a browser. It will spit out a number after a few seconds.
Now you have to set the cost.
Edit: \inc\functions_user.php
Line: 173
Find the following and change the cost number to what cost.php gave you, then delete cost.php.
Note
1. You will have to make core edits
2. You need a minimum of PHP 5.5.0
Before you go any further!
MAKE A BACKUP OF YOUR DATABASE AND THE FILES YOU EDIT!
It's better to be safe than sorry.
Edit: \usercp.php
Line: 1203
Select the following:
PHP Code:
if(validate_password_from_uid($mybb->user['uid'], $mybb->get_input('password')) == false)
PHP Code:
if(validate_password_from_uid($mybb->user['uid'], md5($mybb->get_input('password'))) == false)
Edit: \inc\functions_user.php
Line: 73 - 189
Select the following:
PHP Code:
/**
* Checks a password with a supplied uid.
*
* @param int $uid The user id.
* @param string $password The plain-text password.
* @param array $user An optional user data array.
* @return boolean|array False when not valid, user data array when valid.
*/
function validate_password_from_uid($uid, $password, $user = array())
{
global $db, $mybb;
if(isset($mybb->user['uid']) && $mybb->user['uid'] == $uid)
{
$user = $mybb->user;
}
if(!$user['password'])
{
$query = $db->simple_select("users", "uid,username,password,salt,loginkey,usergroup", "uid='".(int)$uid."'");
$user = $db->fetch_array($query);
}
if(!$user['salt'])
{
// Generate a salt for this user and assume the password stored in db is a plain md5 password
$user['salt'] = generate_salt();
$user['password'] = salt_password($user['password'], $user['salt']);
$sql_array = array(
"salt" => $user['salt'],
"password" => $user['password']
);
$db->update_query("users", $sql_array, "uid='".$user['uid']."'");
}
if(!$user['loginkey'])
{
$user['loginkey'] = generate_loginkey();
$sql_array = array(
"loginkey" => $user['loginkey']
);
$db->update_query("users", $sql_array, "uid = ".$user['uid']);
}
if(salt_password(md5($password), $user['salt']) === $user['password'])
{
return $user;
}
else
{
return false;
}
}
/**
* Updates a user's password.
*
* @param int $uid The user's id.
* @param string $password The md5()'ed password.
* @param string $salt (Optional) The salt of the user.
* @return array The new password.
*/
function update_password($uid, $password, $salt="")
{
global $db, $plugins;
$newpassword = array();
// If no salt was specified, check in database first, if still doesn't exist, create one
if(!$salt)
{
$query = $db->simple_select("users", "salt", "uid='$uid'");
$user = $db->fetch_array($query);
if($user['salt'])
{
$salt = $user['salt'];
}
else
{
$salt = generate_salt();
}
$newpassword['salt'] = $salt;
}
// Create new password based on salt
$saltedpw = salt_password($password, $salt);
// Generate new login key
$loginkey = generate_loginkey();
// Update password and login key in database
$newpassword['password'] = $saltedpw;
$newpassword['loginkey'] = $loginkey;
$db->update_query("users", $newpassword, "uid='$uid'");
$plugins->run_hooks("password_changed");
return $newpassword;
}
/**
* Salts a password based on a supplied salt.
*
* @param string $password The md5()'ed password.
* @param string $salt The salt.
* @return string The password hash.
*/
function salt_password($password, $salt)
{
return md5(md5($salt).$password);
}
/**
* Generates a random salt
*
* @return string The salt.
*/
function generate_salt()
{
return random_str(8);
}
PHP Code:
/**
* Checks a password with a supplied uid.
*
* @param int $uid The user id.
* @param string $password the md5()'ed password.
* @param array $user An optional user data array.
* @return boolean|array False when not valid, user data array when valid.
*/
function validate_password_from_uid($uid, $password, $user = array())
{
global $db, $mybb;
if(isset($mybb->user['uid']) && $mybb->user['uid'] == $uid)
{
$user = $mybb->user;
}
if(!$user['password'])
{
$query = $db->simple_select("users", "uid,username,password,salt,loginkey,usergroup", "uid='".(int)$uid."'");
$user = $db->fetch_array($query);
}
if(!$user['loginkey'])
{
$user['loginkey'] = generate_loginkey();
$sql_array = array(
"loginkey" => $user['loginkey']
);
$db->update_query("users", $sql_array, "uid = ".$user['uid']);
}
if(strlen($user['password']) === 32)
{
if(!$user['salt'])
{
if ($password !== $user['password'])
return false;
}
else
{
if (md5(md5($user['salt']).$password) !== $user['password'])
return false;
}
$user['password'] = bcrypt_password($password);
$sql_array = array(
"salt" => Bcrypt,
"password" => $user['password']
);
$db->update_query("users", $sql_array, "uid='".$user['uid']."'");
return $user;
}
if(password_verify(md5($password), $user['password']))
{
return $user;
}
else
{
return false;
}
return false;
}
/**
* Updates a user's password.
*
* @param int $uid The user's id.
* @param string $password the md5()'ed password.
* @param string $salt (Optional) The salt of the user.
* @return array The new password.
*/
function update_password($uid, $password, $salt="")
{
global $db, $plugins;
// Create new password
$user['password'] = bcrypt_password($password);
// Generate new login key
$user['loginkey'] = generate_loginkey();
$sql_array = array(
"salt" => Bcrypt,
"password" => $user['password'],
"loginkey" => $user['loginkey']
);
$db->update_query("users", $sql_array, "uid='$uid'");
$plugins->run_hooks("password_changed");
return $user;
}
/**
* Bcrypt password.
*
* @param string $password the md5()'ed password.
* @param string $salt The salt.
* @return string The password hash.
*/
function bcrypt_password($password)
{
$options = array('cost' => 11);
return password_hash($password, PASSWORD_BCRYPT, $options);
}
/**
* Dummy Bcrypt salt
*
* @return string The salt.
*/
function generate_salt()
{
return 'Bcrypt';
}
Edit: \inc\datahandlers\user.php
Line: 222 - 223
Find:
PHP Code:
// Combine the password and salt
$user['saltedpw'] = salt_password($user['md5password'], $user['salt']);
PHP Code:
// Bcrypt Password
$user['saltedpw'] = bcrypt_password($user['md5password']);
Once you have done everything above, you can calculate the cost for Bcrypt (Encryption time). This all depends on your server hardware.
Open a new text document, Copy & Paste the below into it, save as cost.php, upload to your server and access it in a browser. It will spit out a number after a few seconds.
PHP Code:
<?php
/**
* This code will benchmark your server to determine how high of a cost you can
* afford. You want to set the highest cost that you can without slowing down
* you server too much. 8-10 is a good baseline, and more is good if your servers
* are fast enough. The code below aims for ≤ 50 milliseconds stretching time,
* which is a good baseline for systems handling interactive logins.
*/
$timeTarget = 0.05; // 50 milliseconds
$cost = 8;
do {
$cost++;
$start = microtime(true);
password_hash("test", PASSWORD_BCRYPT, ["cost" => $cost]);
$end = microtime(true);
} while (($end - $start) < $timeTarget);
echo "Appropriate Cost Found: " . $cost . "\n";
Now you have to set the cost.
Edit: \inc\functions_user.php
Line: 173
Find the following and change the cost number to what cost.php gave you, then delete cost.php.
PHP Code:
$options = array('cost' => 11);