Permalink
Browse files

Updater: Convert legacy password hashes

By wrapping the old hashes in a secure hash (prefixed with a little
proprietary marker), we can store them safely without needing to ask
users to reset their password.

The passwords will later be rehashed to their simple form during login.
  • Loading branch information...
franzliedke committed Jan 3, 2019
1 parent 4dd1a00 commit faef574b37d35e716740e6217cb66f5459ef1ef6
Showing with 53 additions and 8 deletions.
  1. +50 −6 db_update.php
  2. +1 −1 include/common.php
  3. +1 −1 install.php
  4. +1 −0 lang/English/update.php
@@ -9,7 +9,7 @@
// The FluxBB version this script updates to
define('UPDATE_TO', '1.5.10');
define('UPDATE_TO_DB_REVISION', 22);
define('UPDATE_TO_DB_REVISION', 23);
define('UPDATE_TO_SI_REVISION', 2);
define('UPDATE_TO_PARSER_REVISION', 2);
@@ -117,6 +117,9 @@
if (version_compare($cur_version, '1.2', '<'))
error(sprintf($lang_update['Version mismatch error'], $db_name));
if (!isset($password_hash_cost))
error(sprintf($lang_update['Password cost missing error']));
// Do some DB type specific checks
$mysql = false;
switch ($db_type)
@@ -677,10 +680,6 @@ function convert_table_utf8($table, $callback, $old_charset, $key = null, $start
$db->alter_field('users', 'msn', 'VARCHAR(80)', true) or error('Unable to alter msn field', __FILE__, __LINE__, $db->error());
$db->alter_field('users', 'activate_string', 'VARCHAR(80)', true) or error('Unable to alter activate_string field', __FILE__, __LINE__, $db->error());
// Make password field VARCHAR(255) to support password_hash
// 255 is recommended by the PHP manual: http://php.net/manual/en/function.password-hash.php
$db->alter_field('users', 'password', 'VARCHAR(255)', false) or error('Unable to alter password field', __FILE__, __LINE__, $db->error());
// Make all IP fields VARCHAR(39) to support IPv6
$db->alter_field('posts', 'poster_ip', 'VARCHAR(39)', true) or error('Unable to alter poster_ip field', __FILE__, __LINE__, $db->error());
$db->alter_field('users', 'registration_ip', 'VARCHAR(39)', false, '0.0.0.0') or error('Unable to alter registration_ip field', __FILE__, __LINE__, $db->error());
@@ -1714,7 +1713,7 @@ function _error_users($cur_user)
// Preparse signatures
case 'preparse_sigs':
$query_str = '?stage=rebuild_idx';
$query_str = '?stage=harden_passwords';
// If we don't need to parse the sigs, skip this stage
if (isset($pun_config['o_parser_revision']) && $pun_config['o_parser_revision'] >= UPDATE_TO_PARSER_REVISION)
@@ -1746,6 +1745,51 @@ function _error_users($cur_user)
break;
// Convert legacy passwords
case 'harden_passwords':
$query_str = '?stage=rebuild_idx';
// Make password field VARCHAR(255) to support password_hash
// 255 is recommended by the PHP manual: http://php.net/manual/en/function.password-hash.php
if ($start_at == 0)
$db->alter_field('users', 'password', 'VARCHAR(255)', false) or error('Unable to alter password field', __FILE__, __LINE__, $db->error());
// Fetch passwords in batches
$result = $db->query('SELECT * FROM '.$db->prefix.'users WHERE id>'.$start_at.' ORDER BY id ASC LIMIT '.PER_PAGE, false) or error('Unable to fetch user password hashes', __FILE__, __LINE__, $db->error());
while ($cur_user = $db->fetch_assoc($result))
{
$old_password = $cur_user['password'];
$remove_salt = !empty($cur_user['salt']);
if (strlen($old_password) == 32) // MD5 from 1.2
$new_password_hash = '#MD5#'.flux_password_hash($old_password);
else if ($remove_salt) // Salted SHA1 from 1.3
$new_password_hash = '#SHA1-S#'.$cur_user['salt'].'#'.flux_password_hash($old_password);
else if (strlen($old_password) == 40) // Unsalted SHA1 from 1.4
$new_password_hash = '#SHA1#'.flux_password_hash($old_password);
else
$new_password_hash = $old_password;
$db->query('UPDATE '.$db->prefix.'users SET '.($remove_salt ? 'salt=NULL,' : '').' password=\''.$db->escape($new_password_hash).'\' WHERE id='.$cur_user['id']) or error('Unable to save updated password', __FILE__, __LINE__, $db->error());
$end_at = $cur_user['id'];
}
if ($end_at > 0)
{
$result = $db->query('SELECT 1 FROM '.$db->prefix.'users WHERE id>'.$end_at.' ORDER BY id ASC LIMIT 1') or error('Unable to check for next row', __FILE__, __LINE__, $db->error());
if ($db->has_rows($result))
$query_str = '?stage=harden_passwords&start_at='.$end_at;
else
$db->drop_field('users', 'salt');
}
else
$db->drop_field('users', 'salt');
break;
// Rebuild the search index
case 'rebuild_idx':
$query_str = '?stage=finish';
@@ -12,7 +12,7 @@
// Define the version and database revision that this code was written for
define('FORUM_VERSION', '1.5.10');
define('FORUM_DB_REVISION', 22);
define('FORUM_DB_REVISION', 23);
define('FORUM_SI_REVISION', 2);
define('FORUM_PARSER_REVISION', 2);
@@ -9,7 +9,7 @@
// The FluxBB version this script installs
define('FORUM_VERSION', '1.5.10');
define('FORUM_DB_REVISION', 22);
define('FORUM_DB_REVISION', 23);
define('FORUM_SI_REVISION', 2);
define('FORUM_PARSER_REVISION', 2);
@@ -18,6 +18,7 @@
'You are running error' => 'You are running %1$s version %2$s. FluxBB %3$s requires at least %1$s %4$s to run properly. You must upgrade your %1$s installation before you can continue.',
'Version mismatch error' => 'Version mismatch. The database \'%s\' doesn\'t seem to be running a FluxBB database schema supported by this update script.',
'Password cost missing error' => 'Update cannot proceed before you add a line containing <code>$password_hash_cost = 10;</code> to your config.php file in the FluxBB root directory',
'Invalid file error' => 'Invalid database file name. When using SQLite the database file name must be entered exactly as it appears in your \'%s\'',
'Invalid password error' => 'Invalid database password. To upgrade FluxBB you must enter your database password exactly as it appears in your \'%s\'',
'No password error' => 'No database password provided',

0 comments on commit faef574

Please sign in to comment.