Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add notification by email when an admin logs in from a new IP address. #1027

Merged
merged 7 commits into from
Mar 30, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion public_html/lists/admin/defaultconfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,13 @@
'type' => 'text',
'category' => 'security',
),

'notify_admin_login' => array(
'value' => 1,
'description' => s('Notify admin on login from new location'),
'type' => 'boolean',
'category' => 'security',
'allowempty' => true,
),
// admin addresses are other people who receive copies of subscriptions
'admin_addresses' => array(
'value' => '',
Expand Down
27 changes: 25 additions & 2 deletions public_html/lists/admin/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,10 @@ function mb_strtolower($string)
$msg = $loginresult[1];
} else {
session_regenerate_id();

# invalidate other active sessions
Sql_Query(sprintf('update %s set active = 0 where adminid = %d and active != 0',$GLOBALS['tables']['admin_login'],$loginresult[0]));

$_SESSION['adminloggedin'] = $remoteAddr;
$_SESSION['logindetails'] = array(
'adminname' => $_REQUEST['login'],
Expand All @@ -325,6 +329,19 @@ function mb_strtolower($string)
if (!empty($_POST['page'])) {
$page = preg_replace('/\W+/', '', $_POST['page']);
}

# check if this is a new IP address
$knownIP = Sql_Fetch_Row_Query(sprintf('select * from %s where remote_ip4 = "%s"',$GLOBALS['tables']['admin_login'],$remoteAddr));
if (empty($knownIP[0])) {
notifyNewIPLogin($loginresult[0]);
}
Sql_Query(sprintf('insert into %s (moment,adminid,remote_ip4,remote_ip6,sessionid,active)
values(%d,%d,"%s","%s","%s",1)',
$GLOBALS['tables']['admin_login'],time(),$loginresult[0],$remoteAddr,"",session_id()));




}
//If passwords are encrypted and a password recovery request was made, send mail to the admin of the given email address.
} elseif (isset($_REQUEST['forgotpassword'])) {
Expand Down Expand Up @@ -373,14 +390,20 @@ function mb_strtolower($string)
$_SESSION['logindetails'] = '';
$page = 'login';
} elseif ($_SESSION['adminloggedin'] && $_SESSION['logindetails']) {
$active = Sql_Fetch_Row_Query(sprintf('select active from %s where adminid = %d and (remote_ip4 = "%s" or remote_ip6 = "%s") and sessionid = "%s"',
$GLOBALS['tables']['admin_login'],$_SESSION['logindetails']['id'],$remoteAddr,"",session_id()));
$validate = $GLOBALS['admin_auth']->validateAccount($_SESSION['logindetails']['id']);
if (!$validate[0]) {
if (empty($active[0]) || !$validate[0]) {
logEvent(sprintf($GLOBALS['I18N']->get('invalidated login from %s for %s (error %s)'), $remoteAddr,
$_SESSION['logindetails']['adminname'], $validate[1]));
$_SESSION['adminloggedin'] = '';
$_SESSION['logindetails'] = '';
$page = 'login';
$msg = $validate[1];
if (empty($active[0])) {
$msg = s('Your session was invalidated by a new session in a different browser');
} else {
$msg = $validate[1];
}
}
} else {
$page = 'login';
Expand Down
36 changes: 36 additions & 0 deletions public_html/lists/admin/lib.php
Original file line number Diff line number Diff line change
Expand Up @@ -2486,3 +2486,39 @@ function getClientIP()

return $the_ip;
}


function notifyNewIPLogin($adminId) {

$enabled = getConfig('notify_admin_login');
if (empty($enabled)) {
return;
}
$msg = s('

--------------------------------------------------------------------------------

We noticed a login to your phpList installation at https://%s
from a new location. If this was you, you can delete this message. If you do not recognise
this, please login to your phpList installation and change your password.

-------------------------------------------------------------------------------- ',
$GLOBALS['config']['website']);

$admin_mail = $GLOBALS['admin_auth']->adminEmail($_SESSION['logindetails']['id']);
sendMail($admin_mail, $GLOBALS['installation_name'].' '.s('login from new location'), $msg, system_messageheaders($admin_mail));

$ok = sendAdminCopy(s('login from new location'), "\n".$msg);
if ($ok === 0) {
$main_admin_mail = getConfig('admin_address');
logEvent(sprintf('Error sending login notification to %s', $admin_mail));

$msg = s('
---------------------
phpList tried sending the below message to '.$admin_mail.'
but this failed.
------------------').PHP_EOL.PHP_EOL.$msg;
sendMail($main_admin_mail, $GLOBALS['installation_name'].' '.s('login from new location'), $msg, system_messageheaders($admin_mail));
}

}
13 changes: 8 additions & 5 deletions public_html/lists/admin/phpListAdminAuthentication.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,15 @@ public function validateLogin($login, $password)
$passwordDB = $admindata['password'];
//Password encryption verification.
if (strlen($passwordDB) < $GLOBALS['hash_length']) { // Passwords are encrypted but the actual is not.
return array(0, s('incorrect password'));

// the below is actually insecure, it allows resetting the password without approval, so remove
//Encrypt the actual DB password before performing the validation below.
$encryptedPassDB = hash(HASH_ALGO, $passwordDB);
$query = sprintf('update %s set password = "%s" where loginname = "%s"', $GLOBALS['tables']['admin'],
$encryptedPassDB, sql_escape($login));
$passwordDB = $encryptedPassDB;
$req = Sql_Query($query);
// $encryptedPassDB = hash(HASH_ALGO, $passwordDB);
// $query = sprintf('update %s set password = "%s" where loginname = "%s"', $GLOBALS['tables']['admin'],
// $encryptedPassDB, sql_escape($login));
// $passwordDB = $encryptedPassDB;
// $req = Sql_Query($query);
}

if ($admindata['disabled']) {
Expand Down
10 changes: 9 additions & 1 deletion public_html/lists/admin/structure.php
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,15 @@
'admin' => array('integer', "Admin's Id"),
'key_value' => array('varchar (32) not null', 'Key'),
),
/*
'admin_login' => array(
'id' => array('integer not null primary key auto_increment', 'Id'),
'moment' => array('bigint', 'epoch when it happened'),
'adminid' => array('integer', "Admin Id"),
'remote_ip4' => array('varchar (32) not null', 'IPv4 address'),
'remote_ip6' => array('varchar (50) not null', 'IPv6 address'),
'sessionid' => array('varchar (50) not null', 'Session ID'),
'active' => array('tinyint', 'is this login active'),
), /*
* obsolete tables
"task" => array(
"id" => array("integer not null primary key auto_increment","ID"),
Expand Down
5 changes: 5 additions & 0 deletions public_html/lists/admin/upgrade.php
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,11 @@ function output($message)
Sql_Query("alter table {$GLOBALS['tables']['admin']} modify modifiedby varchar(66) default ''");
}

if (!Sql_Table_exists($GLOBALS['tables']['admin_login'])) {
cl_output(s('Creating new table "admin_login"'));
createTable('admin_login');
}

//# longblobs are better at mixing character encoding. We don't know the encoding of anything we may want to store in cache
//# before converting, it's quickest to clear the cache
clearPageCache();
Expand Down
Loading