From c53c2fffa59291556fe267bb8a5c7d624c7e3739 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sat, 20 Jan 2018 22:24:58 -0500 Subject: [PATCH 1/4] [database] Add user.legacy_password field --- boot.php | 2 +- database.sql | 3 ++- src/Database/DBStructure.php | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/boot.php b/boot.php index 4d2eac35a3..eb3d4e9734 100644 --- a/boot.php +++ b/boot.php @@ -41,7 +41,7 @@ define('FRIENDICA_PLATFORM', 'Friendica'); define('FRIENDICA_CODENAME', 'Asparagus'); define('FRIENDICA_VERSION', '3.6-dev'); define('DFRN_PROTOCOL_VERSION', '2.23'); -define('DB_UPDATE_VERSION', 1243); +define('DB_UPDATE_VERSION', 1244); define('NEW_UPDATE_ROUTINE_VERSION', 1170); /** diff --git a/database.sql b/database.sql index 054bcd9a17..dbd59ee63a 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 3.6-dev (Asparagus) --- DB_UPDATE_VERSION 1243 +-- DB_UPDATE_VERSION 1244 -- ------------------------------------------ @@ -1006,6 +1006,7 @@ CREATE TABLE IF NOT EXISTS `user` ( `guid` varchar(64) NOT NULL DEFAULT '' COMMENT '', `username` varchar(255) NOT NULL DEFAULT '' COMMENT '', `password` varchar(255) NOT NULL DEFAULT '' COMMENT '', + `legacy_password` tinyint NOT NULL DEFAULT 0 COMMENT 'Is the password hash double-hashed?', `nickname` varchar(255) NOT NULL DEFAULT '' COMMENT '', `email` varchar(255) NOT NULL DEFAULT '' COMMENT '', `openid` varchar(255) NOT NULL DEFAULT '' COMMENT '', diff --git a/src/Database/DBStructure.php b/src/Database/DBStructure.php index cb69b49525..9dd5e5a515 100644 --- a/src/Database/DBStructure.php +++ b/src/Database/DBStructure.php @@ -1708,6 +1708,7 @@ class DBStructure { "guid" => ["type" => "varchar(64)", "not null" => "1", "default" => "", "comment" => ""], "username" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], "password" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], + "legacy_password" => ["type" => "tinyint", "not null" => "1", "default" => "0", "comment" => "Is the password hash double-hashed?"], "nickname" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], "email" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], "openid" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], From b0a764b14c2f2798f7eb223e58d47530f80609c1 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sat, 20 Jan 2018 22:29:03 -0500 Subject: [PATCH 2/4] Use password_hash() for passwords - Use legacy_password to update double-hashed passwords --- src/Model/User.php | 46 ++++++++++++++++++++++++++++++++++----------- src/Util/ExAuth.php | 2 +- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/src/Model/User.php b/src/Model/User.php index 382ec62cc2..bbe424ecbc 100644 --- a/src/Model/User.php +++ b/src/Model/User.php @@ -112,7 +112,7 @@ class User if (is_object($user_info)) { $user = (array) $user_info; } elseif (is_int($user_info)) { - $user = dba::selectFirst('user', ['uid', 'password'], + $user = dba::selectFirst('user', ['uid', 'password', 'legacy_password'], [ 'uid' => $user_info, 'blocked' => 0, @@ -122,7 +122,7 @@ class User ] ); } elseif (is_string($user_info)) { - $user = dba::fetch_first('SELECT `uid`, `password` + $user = dba::fetch_first('SELECT `uid`, `password`, `legacy_password` FROM `user` WHERE (`email` = ? OR `username` = ? OR `nickname` = ?) AND `blocked` = 0 @@ -138,17 +138,29 @@ class User $user = $user_info; } - if (!DBM::is_result($user) || !isset($user['uid']) || !isset($user['password'])) { - return false; + if (!DBM::is_result($user) + || !isset($user['uid']) + || !isset($user['password']) + || !isset($user['legacy_password']) + ) { + throw new Exception('Not enough information to authenticate'); } - $password_hashed = self::hashPassword($password); + if ($user['legacy_password']) { + if (password_verify(self::hashPasswordLegacy($password), $user['password'])) { + self::updatePassword($user['uid'], $password); - if ($password_hashed !== $user['password']) { - return false; + return $user['uid']; + } + } elseif (password_verify($password, $user['password'])) { + if (password_needs_rehash($user['password'], PASSWORD_DEFAULT)) { + self::updatePassword($user['uid'], $password); + } + + return $user['uid']; } - return $user['uid']; + return false; } /** @@ -161,15 +173,26 @@ class User return autoname(6) . mt_rand(100, 9999); } + /** + * Legacy hashing function, kept for password migration purposes + * + * @param string $password + * @return string + */ + private static function hashPasswordLegacy($password) + { + return hash('whirlpool', $password); + } + /** * Global user password hashing function * * @param string $password * @return string */ - private static function hashPassword($password) + public static function hashPassword($password) { - return hash('whirlpool', $password); + return password_hash($password, PASSWORD_DEFAULT); } /** @@ -197,7 +220,8 @@ class User $fields = [ 'password' => $pasword_hashed, 'pwdreset' => null, - 'pwdreset_time' => null + 'pwdreset_time' => null, + 'legacy_password' => false ]; return dba::update('user', $fields, ['uid' => $uid]); } diff --git a/src/Util/ExAuth.php b/src/Util/ExAuth.php index 847059d6d7..8940c02055 100644 --- a/src/Util/ExAuth.php +++ b/src/Util/ExAuth.php @@ -226,7 +226,7 @@ class ExAuth if ($a->get_hostname() == $aCommand[2]) { $this->writeLog(LOG_INFO, 'internal auth for ' . $sUser . '@' . $aCommand[2]); - $aUser = dba::selectFirst('user', ['uid', 'password'], ['nickname' => $sUser]); + $aUser = dba::selectFirst('user', ['uid', 'password', 'legacy_password'], ['nickname' => $sUser]); if (DBM::is_result($aUser)) { $uid = $aUser['uid']; $success = User::authenticate($aUser, $aCommand[3]); From 2bfd9f9d87cee6a7b9ed0739cf12d1350b47f0fe Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sat, 20 Jan 2018 22:37:13 -0500 Subject: [PATCH 3/4] Add database update function to use new hashes --- update.php | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/update.php b/update.php index 3cfc39e192..00b8d890ec 100644 --- a/update.php +++ b/update.php @@ -5,8 +5,7 @@ use Friendica\Core\Config; use Friendica\Core\PConfig; use Friendica\Core\Worker; use Friendica\Database\DBM; -use Friendica\Model\Photo; -use Friendica\Object\Image; +use Friendica\Model\User; /** * @@ -146,3 +145,19 @@ function update_1203() { $r = q("UPDATE `user` SET `account-type` = %d WHERE `page-flags` IN (%d, %d)", dbesc(ACCOUNT_TYPE_COMMUNITY), dbesc(PAGE_COMMUNITY), dbesc(PAGE_PRVGROUP)); } + +function update_1244() { + // Sets legacy_password for all legacy hashes + dba::update('user', ['legacy_password' => true], ['SUBSTR(password, 1, 4) != "$2y$"']); + + // All legacy hashes are re-hashed using the new secure hashing function + $stmt = dba::select('user', ['uid', 'password'], ['legacy_password' => true]); + while($user = dba::fetch($stmt)) { + dba::update('user', ['password' => User::hashPassword($user['password'])], ['uid' => $user['uid']]); + } + + // Logged in users are forcibly logged out + dba::delete('session', ['1 = 1']); + + return UPDATE_SUCCESS; +} From 4fb24d4ebba267309dc76f3107bd7cc891af6c17 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Mon, 22 Jan 2018 07:48:30 -0500 Subject: [PATCH 4/4] Make the user.legacy_password field boolean --- database.sql | 2 +- src/Database/DBStructure.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/database.sql b/database.sql index dbd59ee63a..506e252ecb 100644 --- a/database.sql +++ b/database.sql @@ -1006,7 +1006,7 @@ CREATE TABLE IF NOT EXISTS `user` ( `guid` varchar(64) NOT NULL DEFAULT '' COMMENT '', `username` varchar(255) NOT NULL DEFAULT '' COMMENT '', `password` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `legacy_password` tinyint NOT NULL DEFAULT 0 COMMENT 'Is the password hash double-hashed?', + `legacy_password` boolean NOT NULL DEFAULT 0 COMMENT 'Is the password hash double-hashed?', `nickname` varchar(255) NOT NULL DEFAULT '' COMMENT '', `email` varchar(255) NOT NULL DEFAULT '' COMMENT '', `openid` varchar(255) NOT NULL DEFAULT '' COMMENT '', diff --git a/src/Database/DBStructure.php b/src/Database/DBStructure.php index 9dd5e5a515..b3eed0dd1b 100644 --- a/src/Database/DBStructure.php +++ b/src/Database/DBStructure.php @@ -1708,7 +1708,7 @@ class DBStructure { "guid" => ["type" => "varchar(64)", "not null" => "1", "default" => "", "comment" => ""], "username" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], "password" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], - "legacy_password" => ["type" => "tinyint", "not null" => "1", "default" => "0", "comment" => "Is the password hash double-hashed?"], + "legacy_password" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Is the password hash double-hashed?"], "nickname" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], "email" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], "openid" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],