Merge pull request #4293 from MrPetovan/task/3942-switch-to-password-hash()
Migrate password hashing Part 3: Switch to password hash
This commit is contained in:
commit
e2e38fcd51
6 changed files with 57 additions and 16 deletions
2
boot.php
2
boot.php
|
@ -41,7 +41,7 @@ define('FRIENDICA_PLATFORM', 'Friendica');
|
||||||
define('FRIENDICA_CODENAME', 'Asparagus');
|
define('FRIENDICA_CODENAME', 'Asparagus');
|
||||||
define('FRIENDICA_VERSION', '3.6-dev');
|
define('FRIENDICA_VERSION', '3.6-dev');
|
||||||
define('DFRN_PROTOCOL_VERSION', '2.23');
|
define('DFRN_PROTOCOL_VERSION', '2.23');
|
||||||
define('DB_UPDATE_VERSION', 1243);
|
define('DB_UPDATE_VERSION', 1244);
|
||||||
define('NEW_UPDATE_ROUTINE_VERSION', 1170);
|
define('NEW_UPDATE_ROUTINE_VERSION', 1170);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
-- ------------------------------------------
|
-- ------------------------------------------
|
||||||
-- Friendica 3.6-dev (Asparagus)
|
-- 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 '',
|
`guid` varchar(64) NOT NULL DEFAULT '' COMMENT '',
|
||||||
`username` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
`username` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||||
`password` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
`password` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||||
|
`legacy_password` boolean NOT NULL DEFAULT 0 COMMENT 'Is the password hash double-hashed?',
|
||||||
`nickname` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
`nickname` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||||
`email` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
`email` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||||
`openid` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
`openid` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||||
|
|
|
@ -1708,6 +1708,7 @@ class DBStructure {
|
||||||
"guid" => ["type" => "varchar(64)", "not null" => "1", "default" => "", "comment" => ""],
|
"guid" => ["type" => "varchar(64)", "not null" => "1", "default" => "", "comment" => ""],
|
||||||
"username" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
|
"username" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
|
||||||
"password" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
|
"password" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
|
||||||
|
"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" => ""],
|
"nickname" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
|
||||||
"email" => ["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" => ""],
|
"openid" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
|
||||||
|
|
|
@ -112,7 +112,7 @@ class User
|
||||||
if (is_object($user_info)) {
|
if (is_object($user_info)) {
|
||||||
$user = (array) $user_info;
|
$user = (array) $user_info;
|
||||||
} elseif (is_int($user_info)) {
|
} elseif (is_int($user_info)) {
|
||||||
$user = dba::selectFirst('user', ['uid', 'password'],
|
$user = dba::selectFirst('user', ['uid', 'password', 'legacy_password'],
|
||||||
[
|
[
|
||||||
'uid' => $user_info,
|
'uid' => $user_info,
|
||||||
'blocked' => 0,
|
'blocked' => 0,
|
||||||
|
@ -122,7 +122,7 @@ class User
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
} elseif (is_string($user_info)) {
|
} elseif (is_string($user_info)) {
|
||||||
$user = dba::fetch_first('SELECT `uid`, `password`
|
$user = dba::fetch_first('SELECT `uid`, `password`, `legacy_password`
|
||||||
FROM `user`
|
FROM `user`
|
||||||
WHERE (`email` = ? OR `username` = ? OR `nickname` = ?)
|
WHERE (`email` = ? OR `username` = ? OR `nickname` = ?)
|
||||||
AND `blocked` = 0
|
AND `blocked` = 0
|
||||||
|
@ -138,17 +138,29 @@ class User
|
||||||
$user = $user_info;
|
$user = $user_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!DBM::is_result($user) || !isset($user['uid']) || !isset($user['password'])) {
|
if (!DBM::is_result($user)
|
||||||
return false;
|
|| !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 $user['uid'];
|
||||||
return false;
|
}
|
||||||
|
} 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);
|
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
|
* Global user password hashing function
|
||||||
*
|
*
|
||||||
* @param string $password
|
* @param string $password
|
||||||
* @return string
|
* @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 = [
|
$fields = [
|
||||||
'password' => $pasword_hashed,
|
'password' => $pasword_hashed,
|
||||||
'pwdreset' => null,
|
'pwdreset' => null,
|
||||||
'pwdreset_time' => null
|
'pwdreset_time' => null,
|
||||||
|
'legacy_password' => false
|
||||||
];
|
];
|
||||||
return dba::update('user', $fields, ['uid' => $uid]);
|
return dba::update('user', $fields, ['uid' => $uid]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -226,7 +226,7 @@ class ExAuth
|
||||||
if ($a->get_hostname() == $aCommand[2]) {
|
if ($a->get_hostname() == $aCommand[2]) {
|
||||||
$this->writeLog(LOG_INFO, 'internal auth for ' . $sUser . '@' . $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)) {
|
if (DBM::is_result($aUser)) {
|
||||||
$uid = $aUser['uid'];
|
$uid = $aUser['uid'];
|
||||||
$success = User::authenticate($aUser, $aCommand[3]);
|
$success = User::authenticate($aUser, $aCommand[3]);
|
||||||
|
|
19
update.php
19
update.php
|
@ -5,8 +5,7 @@ use Friendica\Core\Config;
|
||||||
use Friendica\Core\PConfig;
|
use Friendica\Core\PConfig;
|
||||||
use Friendica\Core\Worker;
|
use Friendica\Core\Worker;
|
||||||
use Friendica\Database\DBM;
|
use Friendica\Database\DBM;
|
||||||
use Friendica\Model\Photo;
|
use Friendica\Model\User;
|
||||||
use Friendica\Object\Image;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -146,3 +145,19 @@ function update_1203() {
|
||||||
$r = q("UPDATE `user` SET `account-type` = %d WHERE `page-flags` IN (%d, %d)",
|
$r = q("UPDATE `user` SET `account-type` = %d WHERE `page-flags` IN (%d, %d)",
|
||||||
dbesc(ACCOUNT_TYPE_COMMUNITY), dbesc(PAGE_COMMUNITY), dbesc(PAGE_PRVGROUP));
|
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;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue