From b9b0440186ea13687c5af0ed4a7dece37e151d8a Mon Sep 17 00:00:00 2001 From: Marcus Hill Date: Sat, 21 Jan 2023 13:25:16 +0000 Subject: [PATCH] - Add email notification to agents if their 2FA code is entered incorrectly (this may be a sign of account compromise) - Tidy login code flow so that the "logged" session variable only has to be set in one place, rather than in two (both for 2fa and non-2fa logins) --- login.php | 249 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 143 insertions(+), 106 deletions(-) diff --git a/login.php b/login.php index 0e88297b..4ac43edd 100644 --- a/login.php +++ b/login.php @@ -12,6 +12,20 @@ include("functions.php"); $ip = strip_tags(mysqli_real_escape_string($mysqli,get_ip())); $user_agent = strip_tags(mysqli_real_escape_string($mysqli,$_SERVER['HTTP_USER_AGENT'])); +// Query Settings for "default" company (as companies are being removed shortly) +$sql_settings = mysqli_query($mysqli,"SELECT * FROM settings WHERE company_id = 1"); +$row = mysqli_fetch_array($sql_settings); + +// Mail +$config_smtp_host = $row['config_smtp_host']; +$config_smtp_port = $row['config_smtp_port']; +$config_smtp_encryption = $row['config_smtp_encryption']; +$config_smtp_username = $row['config_smtp_username']; +$config_smtp_password = $row['config_smtp_password']; +$config_mail_from_email = $row['config_mail_from_email']; +$config_mail_from_name = $row['config_mail_from_name']; + + // HTTP-Only cookies ini_set("session.cookie_httponly", True); @@ -48,47 +62,64 @@ if (isset($_POST['login'])) { // Passed login brute force check $email = strip_tags(mysqli_real_escape_string($mysqli, $_POST['email'])); $password = $_POST['password']; + + $current_code = 0; // Default value if (isset($_POST['current_code'])) { $current_code = strip_tags(mysqli_real_escape_string($mysqli, $_POST['current_code'])); } $row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT * FROM users LEFT JOIN user_settings on users.user_id = user_settings.user_id WHERE user_email = '$email' AND user_archived_at IS NULL AND user_status = 1")); + + // Check password if ($row && password_verify($password, $row['user_password'])) { - // User variables - $token = $row['user_token']; + // User password correct (partial login) + + // Set temporary user variables $user_name = strip_tags(mysqli_real_escape_string($mysqli, $row['user_name'])); $user_id = $row['user_id']; + $user_email = $row['user_email']; + $token = $row['user_token']; - // Session info - $_SESSION['user_id'] = $user_id; - $_SESSION['user_name'] = $user_name; - $_SESSION['user_role'] = $row['user_role']; - $_SESSION['csrf_token'] = bin2hex(random_bytes(78)); + // Checking for user 2FA + require_once("rfc6238.php"); + if (empty($token) || TokenAuth6238::verify($token, $current_code)) { - // Setup encryption session key - if (isset($row['user_specific_encryption_ciphertext']) && $row['user_role'] > 1) { - $user_encryption_ciphertext = $row['user_specific_encryption_ciphertext']; - $site_encryption_master_key = decryptUserSpecificKey($user_encryption_ciphertext, $password); - generateUserSessionKey($site_encryption_master_key); + // FULL LOGIN SUCCESS - 2FA not configured or was successful - // Setup extension - if (isset($row['user_extension_key']) && !empty($row['user_extension_key'])) { - // Extension cookie - // Note: Browsers don't accept cookies with SameSite None if they are not HTTPS. - setcookie("user_extension_key", "$row[user_extension_key]", ['path' => '/', 'secure' => true, 'httponly' => true, 'samesite' => 'None']); - - // Set PHP session in DB so we can access the session encryption data (above) - $user_php_session = session_id(); - mysqli_query($mysqli, "UPDATE users SET user_php_session = '$user_php_session' WHERE user_id = '$user_id'"); + // Determine whether 2FA was used (for logs) + $extended_log = ''; // Default value + if ($current_code !== 0 ) { + $extended_log = 'with 2FA'; } - } - if (empty($token)) { - // Full Login successful + // Logging successful login + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Login', log_action = 'Success', log_description = '$user_name successfully logged in $extended_log', log_ip = '$ip', log_user_agent = '$user_agent', log_user_id = $user_id"); + // Session info + $_SESSION['user_id'] = $user_id; + $_SESSION['user_name'] = $user_name; + $_SESSION['user_role'] = $row['user_role']; + $_SESSION['csrf_token'] = bin2hex(random_bytes(78)); $_SESSION['logged'] = TRUE; - mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Login', log_action = 'Success', log_description = '$user_name successfully logged in', log_ip = '$ip', log_user_agent = '$user_agent', log_user_id = $user_id"); + + // Setup encryption session key + if (isset($row['user_specific_encryption_ciphertext']) && $row['user_role'] > 1) { + $user_encryption_ciphertext = $row['user_specific_encryption_ciphertext']; + $site_encryption_master_key = decryptUserSpecificKey($user_encryption_ciphertext, $password); + generateUserSessionKey($site_encryption_master_key); + + // Setup extension + if (isset($row['user_extension_key']) && !empty($row['user_extension_key'])) { + // Extension cookie + // Note: Browsers don't accept cookies with SameSite None if they are not HTTPS. + setcookie("user_extension_key", "$row[user_extension_key]", ['path' => '/', 'secure' => true, 'httponly' => true, 'samesite' => 'None']); + + // Set PHP session in DB so we can access the session encryption data (above) + $user_php_session = session_id(); + mysqli_query($mysqli, "UPDATE users SET user_php_session = '$user_php_session' WHERE user_id = '$user_id'"); + } + } // Show start page/dashboard depending on role if ($row['user_role'] == 2) { @@ -97,53 +128,59 @@ if (isset($_POST['login'])) { header("Location: dashboard_financial.php"); } + } else { - // Prompt for MFA - $token_field = "
- -
-
- -
-
-
"; + // MFA is configured and needs to be confirmed, or was unsuccessful - require_once("rfc6238.php"); + // HTML code for the token input field + $token_field = " +
+ +
+
+ +
+
+
"; - if (TokenAuth6238::verify($token, $current_code)) { - // Full login (with MFA) successful - $_SESSION['logged'] = TRUE; - mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Login 2FA', log_action = 'Success', log_description = '$user_name successfully logged in using 2FA', log_ip = '$ip', log_user_agent = '$user_agent', log_created_at = NOW(), log_user_id = $user_id"); + // Log/notify if MFA was unsuccessful + if ($current_code !== 0) { - // Show start page/dashboard depending on role - if ($row['user_role'] == 2) { - header("Location: dashboard_technical.php"); - } else { - header("Location: dashboard_financial.php"); - } - - } else { + // Logging mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Login', log_action = '2FA Failed', log_description = '$user_name failed 2FA', log_ip = '$ip', log_user_agent = '$user_agent', log_created_at = NOW(), log_user_id = $user_id"); + // Email the tech to advise their credentials may be compromised + if (!empty($config_smtp_host)) { + $subject = "Important: ITFlow failed 2FA login attempt for $user_name"; + $body = "Hi $user_name,

A recent login to ITFlow was unsuccessful due to an incorrect 2FA code. If you did not attempt this login, your credentials may be compromised.

Thanks,
ITFlow"; + + $mail = sendSingleEmail($config_smtp_host, $config_smtp_username, $config_smtp_password, $config_smtp_encryption, $config_smtp_port, + $config_mail_from_email, $config_mail_from_name, + $user_email, $user_name, + $subject, $body); + } + + // HTML feedback for incorrect 2FA code $response = " -
- Please Enter 2FA Key! - -
- "; +
+ Please Enter 2FA Key! + +
"; } } } else { + + // Password incorrect or user doesn't exist - show generic error + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Login', log_action = 'Failed', log_description = 'Failed login attempt using $email', log_ip = '$ip', log_user_agent = '$user_agent', log_created_at = NOW()"); $response = " -
- Incorrect username or password. - -
- "; +
+ Incorrect username or password. + +
"; } } } @@ -153,60 +190,60 @@ if (isset($_POST['login'])) { - - - <?php echo $config_app_name; ?> | Login - - - + + + <?php echo $config_app_name; ?> | Login + + + - - - - - - + + + + + +
- - - -