From 6f419001ddf12c51f83b001721c125eaeb7e8bc2 Mon Sep 17 00:00:00 2001 From: Marcus Hill Date: Mon, 10 Jan 2022 21:12:18 +0000 Subject: [PATCH 01/22] Add database column for user encrypt ciphertext --- db.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/db.sql b/db.sql index 2e05140e..2945c349 100644 --- a/db.sql +++ b/db.sql @@ -1163,6 +1163,7 @@ CREATE TABLE `users` ( `user_email` varchar(200) NOT NULL, `user_password` varchar(200) NOT NULL, `user_token` varchar(200) DEFAULT NULL, + `user_encryption_ciphertext` varchar(200) DEFAULT NULL, `user_avatar` varchar(200) DEFAULT NULL, `user_created_at` datetime NOT NULL, `user_updated_at` datetime DEFAULT NULL, From 4aaa39b3bb4e147ca0bdaa5cbac2e6e91af451ca Mon Sep 17 00:00:00 2001 From: Marcus Hill Date: Mon, 10 Jan 2022 21:25:43 +0000 Subject: [PATCH 02/22] Rename column --- db.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db.sql b/db.sql index 2945c349..12ddbb03 100644 --- a/db.sql +++ b/db.sql @@ -1163,7 +1163,7 @@ CREATE TABLE `users` ( `user_email` varchar(200) NOT NULL, `user_password` varchar(200) NOT NULL, `user_token` varchar(200) DEFAULT NULL, - `user_encryption_ciphertext` varchar(200) DEFAULT NULL, + `user_specific_encryption_ciphertext` varchar(200) DEFAULT NULL, `user_avatar` varchar(200) DEFAULT NULL, `user_created_at` datetime NOT NULL, `user_updated_at` datetime DEFAULT NULL, From 00be27f8f48008e2579f8f10a198d820fd9e576c Mon Sep 17 00:00:00 2001 From: Marcus Hill Date: Mon, 10 Jan 2022 21:46:30 +0000 Subject: [PATCH 03/22] Remove old AES key column --- db.sql | 1 - 1 file changed, 1 deletion(-) diff --git a/db.sql b/db.sql index 12ddbb03..e1b11790 100644 --- a/db.sql +++ b/db.sql @@ -889,7 +889,6 @@ DROP TABLE IF EXISTS `settings`; CREATE TABLE `settings` ( `company_id` int(11) NOT NULL, `config_api_key` varchar(200) DEFAULT NULL, - `config_aes_key` varchar(250) DEFAULT NULL, `config_base_url` varchar(200) DEFAULT NULL, `config_smtp_host` varchar(200) DEFAULT NULL, `config_smtp_port` int(5) DEFAULT NULL, From 13d83f6e3bffe042fefed34d37cefaef7cd71757 Mon Sep 17 00:00:00 2001 From: Marcus Hill Date: Mon, 10 Jan 2022 21:47:12 +0000 Subject: [PATCH 04/22] Add session key setup --- login.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/login.php b/login.php index 4eeadd03..2ee3d646 100644 --- a/login.php +++ b/login.php @@ -45,6 +45,11 @@ if(isset($_POST['login'])){ $user_name = $row['user_name']; $user_id = $row['user_id']; + //Setup encryption session key + $user_encryption_ciphertext = $row['user_specific_encryption_ciphertext']; + $site_encryption_master_key = decryptUserSpecificKey($user_encryption_ciphertext, $password); + generateUserSessionKey($site_encryption_master_key); + if(empty($token)){ $_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_created_at = NOW(), log_user_id = $user_id"); From a56d701a8af48929ed4c93167ea150510f4e0877 Mon Sep 17 00:00:00 2001 From: Marcus Hill Date: Mon, 10 Jan 2022 21:47:51 +0000 Subject: [PATCH 05/22] Add initial user specific encryption of site master key --- setup.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/setup.php b/setup.php index 496d421f..d5e923ff 100644 --- a/setup.php +++ b/setup.php @@ -399,7 +399,13 @@ if(isset($_POST['add_user'])){ $email = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['email']))); $password = password_hash($_POST['password'], PASSWORD_DEFAULT); - mysqli_query($mysqli,"INSERT INTO users SET user_name = '$name', user_email = '$email', user_password = '$password', user_created_at = NOW()"); + //Generate master encryption key + $site_encryption_master_key = keygen(); + + //Generate user specific key + $user_specific_encryption_ciphertext = setupFirstUserSpecificKey($_POST['password'], $site_encryption_master_key); + + mysqli_query($mysqli,"INSERT INTO users SET user_name = '$name', user_email = '$email', user_password = '$password', user_specific_encryption_ciphertext = '$user_specific_encryption_ciphertext', user_created_at = NOW()"); $user_id = mysqli_insert_id($mysqli); @@ -480,7 +486,6 @@ if(isset($_POST['add_company_settings'])){ $company_id = mysqli_insert_id($mysqli); $config_base_url = $_SERVER['HTTP_HOST'] . dirname($_SERVER['REQUEST_URI']); $config_api_key = keygen(); - $config_aes_key = keygen(); mkdir_missing("uploads/clients/$company_id"); file_put_contents("uploads/clients/$company_id/index.php", ""); @@ -536,7 +541,7 @@ if(isset($_POST['add_company_settings'])){ //Set User Company Permissions mysqli_query($mysqli,"INSERT INTO user_companies SET user_id = $user_id, company_id = $company_id"); - mysqli_query($mysqli,"INSERT INTO settings SET company_id = $company_id, config_invoice_prefix = 'INV-', config_invoice_next_number = 1, config_recurring_prefix = 'REC-', config_recurring_next_number = 1, config_invoice_overdue_reminders = '1,3,7', config_quote_prefix = 'QUO-', config_quote_next_number = 1, config_api_key = '$config_api_key', config_aes_key = '$config_aes_key', config_recurring_auto_send_invoice = 1, config_default_net_terms = 7, config_send_invoice_reminders = 1, config_enable_cron = 0, config_ticket_next_number = 1, config_base_url = '$config_base_url'"); + mysqli_query($mysqli,"INSERT INTO settings SET company_id = $company_id, config_invoice_prefix = 'INV-', config_invoice_next_number = 1, config_recurring_prefix = 'REC-', config_recurring_next_number = 1, config_invoice_overdue_reminders = '1,3,7', config_quote_prefix = 'QUO-', config_quote_next_number = 1, config_api_key = '$config_api_key', config_recurring_auto_send_invoice = 1, config_default_net_terms = 7, config_send_invoice_reminders = 1, config_enable_cron = 0, config_ticket_next_number = 1, config_base_url = '$config_base_url'"); //Create Some Data From 49d895040a20a71398b851a61364900385a61bc6 Mon Sep 17 00:00:00 2001 From: Marcus Hill Date: Mon, 10 Jan 2022 22:07:26 +0000 Subject: [PATCH 06/22] Add per-user password encryption using master key --- client_logins.php | 4 +- functions.php | 131 ++++++++++++++++++++++++++++++++++++++++++++-- post.php | 8 +-- 3 files changed, 134 insertions(+), 9 deletions(-) diff --git a/client_logins.php b/client_logins.php index d56f2468..448f5c29 100644 --- a/client_logins.php +++ b/client_logins.php @@ -41,7 +41,7 @@ if(isset($_GET['o'])){ //Rebuild URL $url_query_strings_sb = http_build_query(array_merge($_GET,array('sb' => $sb, 'o' => $o))); -$sql = mysqli_query($mysqli,"SELECT SQL_CALC_FOUND_ROWS *, AES_DECRYPT(login_password, '$config_aes_key') AS login_password FROM logins +$sql = mysqli_query($mysqli,"SELECT SQL_CALC_FOUND_ROWS * FROM logins WHERE login_client_id = $client_id AND (login_name LIKE '%$q%' OR login_username LIKE '%$q%' OR login_uri LIKE '%$q%') ORDER BY $sb $o LIMIT $record_from, $record_to"); @@ -112,7 +112,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli,"SELECT FOUND_ROWS()")); }else{ $login_username_display = "$login_username"; } - $login_password = htmlentities($row['login_password']); + $login_password = htmlentities(decryptLoginEntry($row['login_password'])); $login_otp_secret = $row['login_otp_secret']; if(empty($login_otp_secret)){ $otp_display = "-"; diff --git a/functions.php b/functions.php index 0bac4894..70a583c9 100644 --- a/functions.php +++ b/functions.php @@ -159,15 +159,15 @@ function get_device(){ } } if ($tablet_browser > 0) { - // do something for tablet devices + //do something for tablet devices return 'Tablet'; } else if ($mobile_browser > 0) { - // do something for mobile devices + //do something for mobile devices return 'Mobile'; } else { - // do something for everything else + //do something for everything else return 'Computer'; } } @@ -287,4 +287,129 @@ function mkdir_missing($dir) { } } +//Called during initial setup +//Encrypts the master key with the user's password +function setupFirstUserSpecificKey($user_password, $site_encryption_master_key){ + $iv = keygen(); + $salt = keygen(); + + //Generate 128-bit (16 byte/char) kdhash of the users password + $user_password_kdhash = hash_pbkdf2('sha256', $user_password, $salt, 100000, 16); + + //Encrypt the master key with the users kdf'd hash and the IV + $ciphertext = openssl_encrypt($site_encryption_master_key, 'aes-128-cbc', $user_password_kdhash, 0, $iv); + + $user_encryption_ciphertext = $salt . $iv . $ciphertext; + + return $user_encryption_ciphertext; +} + +/* +For additional users / password changes +New Users: Requires the admin setting up their account have the their own Specific/Session key configured +Password Changes: Will use the current info in the session. Maybe a good idea force logout users after a password change, so that upon login new session info is set fresh.. +-- This logic will need to be in the pass change function itself +*/ +function encryptUserSpecificKey($user_password){ + $iv = keygen(); + $salt = keygen(); + + //Get the session info. + $user_encryption_session_ciphertext = $_SESSION['user_encryption_session_ciphertext']; + $user_encryption_session_iv = $_SESSION['user_encryption_session_iv']; + $user_encryption_session_key = $_COOKIE['user_encryption_session_key']; + + //Decrypt the session key to get the master key + $site_encryption_master_key = openssl_decrypt($user_encryption_session_ciphertext, 'aes-128-cbc', $user_encryption_session_key, 0, $user_encryption_session_iv); + + //Generate 128-bit (16 byte/char) kdhash of the users (new) password + $user_password_kdhash = hash_pbkdf2('sha256', $user_password, $salt, 100000, 16); + + //Encrypt the master key with the users kdf'd hash and the IV + $user_encryption_ciphertext = openssl_encrypt($site_encryption_master_key, 'aes-128-cbc', $user_password_kdhash, 0, $iv); + + return $user_encryption_ciphertext; + +} + +//Given a ciphertext (incl. IV) and the user's password, returns the site master key +//Ran at login, to facilitate generateUserSessionKey +function decryptUserSpecificKey($user_encryption_ciphertext, $user_password){ + //Get the IV, salt and ciphertext + $salt = substr($user_encryption_ciphertext, 0, 16); + $iv = substr($user_encryption_ciphertext, 16, 16); + $ciphertext = substr($user_encryption_ciphertext, 32); + + //Generate 128-bit (16 byte/char) kdhash of the users password + $user_password_kdhash = hash_pbkdf2('sha256', $user_password, $salt, 100000, 16); + + //Use this hash to get the original/master key + $site_encryption_master_key = openssl_decrypt($ciphertext, 'aes-128-cbc', $user_password_kdhash, 0, $iv); + return $site_encryption_master_key; +} + +/* +Generates what is probably best described as an session key (ephemeral-ish) +- Allows us to store the master key on the server whilst the user is using the application, without prompting to type their password everytime they want to decrypt a credential +- Ciphertext/IV is stored on the server in the users session, encryption key is controlled/provided by the user as a cookie +- Only the user can decrypt their session ciphertext to get the master key +- Encryption key never hits the disk in cleartext +*/ +function generateUserSessionKey($site_encryption_master_key){ + + //Generate both of these using keygen() + $user_encryption_session_key = keygen(); + $user_encryption_session_iv = keygen(); + $user_encryption_session_ciphertext = openssl_encrypt($site_encryption_master_key, 'aes-128-cbc', $user_encryption_session_key, 0, $user_encryption_session_iv); + + //Store ciphertext in the user's session + $_SESSION['user_encryption_session_ciphertext'] = $user_encryption_session_ciphertext; + $_SESSION['user_encryption_session_iv'] = $user_encryption_session_iv; + + //Give the user "their" key as a cookie + setcookie("user_encryption_session_key", $user_encryption_session_key, 0, "/", "", "true", "true"); +} + +//Decrypts an encrypted password (website/asset login), returns it as a string +function decryptLoginEntry($login_password_ciphertext){ + + //Split the login into IV and Ciphertext + $login_iv = substr($login_password_ciphertext, 0, 16); + $login_ciphertext = $salt = substr($login_password_ciphertext, 16); + + //Get the user session info. + $user_encryption_session_ciphertext = $_SESSION['user_encryption_session_ciphertext']; + $user_encryption_session_iv = $_SESSION['user_encryption_session_iv']; + $user_encryption_session_key = $_COOKIE['user_encryption_session_key']; + + //Decrypt the session key to get the master key + $site_encryption_master_key = openssl_decrypt($user_encryption_session_ciphertext, 'aes-128-cbc', $user_encryption_session_key, 0, $user_encryption_session_iv); + + //Decrypt the login password using the master key + $login_password_cleartext = openssl_decrypt($login_ciphertext, 'aes-128-cbc', $site_encryption_master_key, 0, $login_iv); + return $login_password_cleartext; + +} + +//Encrypts a website/asset login password +function encryptLoginEntry($login_password_cleartext){ + $iv = keygen(); + + //Get the user session info. + $user_encryption_session_ciphertext = $_SESSION['user_encryption_session_ciphertext']; + $user_encryption_session_iv = $_SESSION['user_encryption_session_iv']; + $user_encryption_session_key = $_COOKIE['user_encryption_session_key']; + + //Decrypt the session key to get the master key + $site_encryption_master_key = openssl_decrypt($user_encryption_session_ciphertext, 'aes-128-cbc', $user_encryption_session_key, 0, $user_encryption_session_iv); + + //Encrypt the website/asset login using the master key + $ciphertext = openssl_encrypt($login_password_cleartext, 'aes-128-cbc', $site_encryption_master_key, 0, $iv); + + $login_password_ciphertext = $iv . $ciphertext; + return $login_password_ciphertext; +} + + + ?> \ No newline at end of file diff --git a/post.php b/post.php index 31919d47..0ad20415 100644 --- a/post.php +++ b/post.php @@ -4439,14 +4439,14 @@ if(isset($_POST['add_login'])){ $name = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['name']))); $uri = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['uri']))); $username = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['username']))); - $password = trim(mysqli_real_escape_string($mysqli,$_POST['password'])); + $password = trim(mysqli_real_escape_string($mysqli,encryptLoginEntry($_POST['password']))); $otp_secret = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['otp_secret']))); $note = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['note']))); $vendor_id = intval($_POST['vendor']); $asset_id = intval($_POST['asset']); $software_id = intval($_POST['software']); - mysqli_query($mysqli,"INSERT INTO logins SET login_name = '$name', login_uri = '$uri', login_username = '$username', login_password = AES_ENCRYPT('$password','$config_aes_key'), login_otp_secret = '$otp_secret', login_note = '$note', login_created_at = NOW(), login_vendor_id = $vendor_id, login_asset_id = $asset_id, login_software_id = $software_id, login_client_id = $client_id, company_id = $session_company_id"); + mysqli_query($mysqli,"INSERT INTO logins SET login_name = '$name', login_uri = '$uri', login_username = '$username', login_password = '$password', login_otp_secret = '$otp_secret', login_note = '$note', login_created_at = NOW(), login_vendor_id = $vendor_id, login_asset_id = $asset_id, login_software_id = $software_id, login_client_id = $client_id, company_id = $session_company_id"); //Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Login', log_action = 'Created', log_description = '$name', log_created_at = NOW(), company_id = $session_company_id, log_user_id = $session_user_id"); @@ -5681,7 +5681,7 @@ if(isset($_GET['export_client_pdf'])){ $sql_locations = mysqli_query($mysqli,"SELECT * FROM locations WHERE location_client_id = $client_id ORDER BY location_name ASC"); $sql_vendors = mysqli_query($mysqli,"SELECT * FROM vendors WHERE vendor_client_id = $client_id ORDER BY vendor_name ASC"); if(isset($_GET['passwords'])){ - $sql_logins = mysqli_query($mysqli,"SELECT *, AES_DECRYPT(login_password, '$config_aes_key') AS login_password FROM logins WHERE login_client_id = $client_id ORDER BY login_name ASC"); + $sql_logins = mysqli_query($mysqli,"SELECT * FROM logins WHERE login_client_id = $client_id ORDER BY login_name ASC"); } $sql_assets = mysqli_query($mysqli,"SELECT * FROM assets WHERE asset_client_id = $client_id ORDER BY asset_type ASC"); $sql_networks = mysqli_query($mysqli,"SELECT * FROM networks WHERE network_client_id = $client_id ORDER BY network_name ASC"); @@ -6022,7 +6022,7 @@ if(isset($_GET['export_client_pdf'])){ while($row = mysqli_fetch_array($sql_logins)){ $login_name = $row['login_name']; $login_username = $row['login_username']; - $login_password = $row['login_password']; + $login_password = decryptLoginEntry($row['login_password']); $login_uri = $row['login_uri']; $login_note = $row['login_note']; ?> From aac50bdfdb1e912d0a0274bddc8c963a67af5ef6 Mon Sep 17 00:00:00 2001 From: Marcus Hill Date: Mon, 10 Jan 2022 22:55:08 +0000 Subject: [PATCH 07/22] More changes re encryption --- functions.php | 4 +++- get_settings.php | 1 - post.php | 28 +++++++++++++++++++++------- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/functions.php b/functions.php index 70a583c9..de1a80f5 100644 --- a/functions.php +++ b/functions.php @@ -326,7 +326,9 @@ function encryptUserSpecificKey($user_password){ $user_password_kdhash = hash_pbkdf2('sha256', $user_password, $salt, 100000, 16); //Encrypt the master key with the users kdf'd hash and the IV - $user_encryption_ciphertext = openssl_encrypt($site_encryption_master_key, 'aes-128-cbc', $user_password_kdhash, 0, $iv); + $ciphertext = openssl_encrypt($site_encryption_master_key, 'aes-128-cbc', $user_password_kdhash, 0, $iv); + + $user_encryption_ciphertext = $salt . $iv . $ciphertext; return $user_encryption_ciphertext; diff --git a/get_settings.php b/get_settings.php index 0e3d32a7..ba6af6ad 100644 --- a/get_settings.php +++ b/get_settings.php @@ -6,7 +6,6 @@ $row = mysqli_fetch_array($sql_settings); //General $config_api_key = $row['config_api_key']; -$config_aes_key = $row['config_aes_key']; $config_base_url = $row['config_base_url']; //Mail diff --git a/post.php b/post.php index 0ad20415..8494f3e3 100644 --- a/post.php +++ b/post.php @@ -53,10 +53,11 @@ if(isset($_POST['add_user'])){ $name = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['name']))); $email = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['email']))); $password = password_hash($_POST['password'], PASSWORD_DEFAULT); + $user_specific_encryption_ciphertext = encryptUserSpecificKey($password); //TODO: Consider this users role - if they don't need access to logins, potentially don't set this -- but it's a pain to add afterwards. $default_company = intval($_POST['default_company']); $role = intval($_POST['role']); - mysqli_query($mysqli,"INSERT INTO users SET user_name = '$name', user_email = '$email', user_password = '$password', user_created_at = NOW()"); + mysqli_query($mysqli,"INSERT INTO users SET user_name = '$name', user_email = '$email', user_password = '$password', user_specific_encryption_ciphertext = '$user_specific_encryption_ciphertext', user_created_at = NOW()"); $user_id = mysqli_insert_id($mysqli); @@ -188,7 +189,8 @@ if(isset($_POST['edit_user'])){ if(!empty($new_password)){ $new_password = password_hash($new_password, PASSWORD_DEFAULT); - mysqli_query($mysqli,"UPDATE users SET user_password = '$new_password' WHERE user_id = $user_id"); + $user_specific_encryption_ciphertext = encryptUserSpecificKey($_POST['new_password']); + mysqli_query($mysqli,"UPDATE users SET user_password = '$new_password', user_specific_encryption_ciphertext = '$user_specific_encryption_ciphertext' WHERE user_id = $user_id"); //Extended Logging $extended_log_description .= ", password changed"; } @@ -212,6 +214,7 @@ if(isset($_POST['edit_profile'])){ $email = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['email']))); $new_password = trim($_POST['new_password']); $existing_file_name = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['existing_file_name']))); + $logout = FALSE; //Check to see if a file is attached if($_FILES['file']['tmp_name'] != ''){ @@ -265,18 +268,24 @@ if(isset($_POST['edit_profile'])){ if(!empty($new_password)){ $new_password = password_hash($new_password, PASSWORD_DEFAULT); - mysqli_query($mysqli,"UPDATE users SET user_password = '$new_password' WHERE user_id = $user_id"); + $user_specific_encryption_ciphertext = encryptUserSpecificKey($_POST['new_password']); + mysqli_query($mysqli,"UPDATE users SET user_password = '$new_password', user_specific_encryption_ciphertext = '$user_specific_encryption_ciphertext' WHERE user_id = $user_id"); $extended_log_description .= ", password changed"; + $logout = TRUE; } //Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'User Preferences', log_action = 'Modify', log_description = '$session_name modified their preferences$extended_log_description', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_created_at = NOW(), log_user_id = $session_user_id, company_id = $session_company_id"); $_SESSION['alert_message'] = "User preferences updated"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); + if ($logout){ + header('Location: post.php?logout'); + } + else{ + header("Location: " . $_SERVER["HTTP_REFERER"]); + } } if(isset($_POST['edit_user_companies'])){ @@ -4463,14 +4472,14 @@ if(isset($_POST['edit_login'])){ $name = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['name']))); $uri = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['uri']))); $username = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['username']))); - $password = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['password']))); + $password = trim(mysqli_real_escape_string($mysqli,encryptLoginEntry($_POST['password']))); $otp_secret = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['otp_secret']))); $note = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['note']))); $vendor_id = intval($_POST['vendor']); $asset_id = intval($_POST['asset']); $software_id = intval($_POST['software']); - mysqli_query($mysqli,"UPDATE logins SET login_name = '$name', login_uri = '$uri', login_username = '$username', login_password = AES_ENCRYPT('$password','$config_aes_key'), login_otp_secret = '$otp_secret', login_note = '$note', login_updated_at = NOW(), login_vendor_id = $vendor_id, login_asset_id = $asset_id, login_software_id = $software_id WHERE login_id = $login_id AND company_id = $session_company_id"); + mysqli_query($mysqli,"UPDATE logins SET login_name = '$name', login_uri = '$uri', login_username = '$username', login_password = '$password', login_otp_secret = '$otp_secret', login_note = '$note', login_updated_at = NOW(), login_vendor_id = $vendor_id, login_asset_id = $asset_id, login_software_id = $software_id WHERE login_id = $login_id AND company_id = $session_company_id"); //Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Login', log_action = 'Modified', log_description = '$name', log_created_at = NOW(), company_id = $session_company_id, log_user_id = $session_user_id"); @@ -6448,6 +6457,11 @@ if(isset($_GET['logout'])){ session_start(); session_destroy(); + + unset($_COOKIE['user_encryption_session_key']); + setcookie("user_encryption_session_key", '', time() - 3600, "/", "", "true", "true"); + + header('Location: login.php'); } From 51edb7a648996a6cea1734ae9c6084b9d524f305 Mon Sep 17 00:00:00 2001 From: Marcus Hill Date: Tue, 11 Jan 2022 00:13:18 +0000 Subject: [PATCH 08/22] Allow backing up master key --- post.php | 90 ++++++++++++++++++++++++++++++--------------- settings-backup.php | 24 +++++++++++- 2 files changed, 84 insertions(+), 30 deletions(-) diff --git a/post.php b/post.php index 8494f3e3..3690233e 100644 --- a/post.php +++ b/post.php @@ -53,7 +53,7 @@ if(isset($_POST['add_user'])){ $name = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['name']))); $email = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['email']))); $password = password_hash($_POST['password'], PASSWORD_DEFAULT); - $user_specific_encryption_ciphertext = encryptUserSpecificKey($password); //TODO: Consider this users role - if they don't need access to logins, potentially don't set this -- but it's a pain to add afterwards. + $user_specific_encryption_ciphertext = encryptUserSpecificKey($_POST['password']); //TODO: Consider this users role - if they don't need access to logins, potentially don't set this -- just know it's a pain to add afterwards (you'd need to reset their password). $default_company = intval($_POST['default_company']); $role = intval($_POST['role']); @@ -399,7 +399,6 @@ if(isset($_POST['add_company'])){ $company_id = mysqli_insert_id($mysqli); $config_base_url = $_SERVER['HTTP_HOST'] . dirname($_SERVER['REQUEST_URI']); $config_api_key = keygen(); - $config_aes_key = keygen(); mkdir("uploads/clients/$company_id"); mkdir("uploads/expenses/$company_id"); @@ -451,7 +450,7 @@ if(isset($_POST['add_company'])){ //Set User Company Permissions mysqli_query($mysqli,"INSERT INTO user_companies SET user_id = $session_user_id, company_id = $company_id"); - mysqli_query($mysqli,"INSERT INTO settings SET company_id = $company_id, config_invoice_prefix = 'INV-', config_invoice_next_number = 1, config_recurring_prefix = 'REC-', config_recurring_next_number = 1, config_invoice_overdue_reminders = '1,3,7', config_quote_prefix = 'QUO-', config_quote_next_number = 1, config_api_key = '$config_api_key', config_aes_key = '$config_aes_key', config_recurring_auto_send_invoice = 1, config_default_net_terms = 7, config_send_invoice_reminders = 1, config_enable_cron = 0, config_ticket_next_number = 1, config_base_url = '$config_base_url'"); + mysqli_query($mysqli,"INSERT INTO settings SET company_id = $company_id, config_invoice_prefix = 'INV-', config_invoice_next_number = 1, config_recurring_prefix = 'REC-', config_recurring_next_number = 1, config_invoice_overdue_reminders = '1,3,7', config_quote_prefix = 'QUO-', config_quote_next_number = 1, config_api_key = '$config_api_key', config_recurring_auto_send_invoice = 1, config_default_net_terms = 7, config_send_invoice_reminders = 1, config_enable_cron = 0, config_ticket_next_number = 1, config_base_url = '$config_base_url'"); //Create Some Data @@ -664,24 +663,24 @@ if(isset($_POST['verify'])){ if(isset($_POST['edit_general_settings'])){ $config_api_key = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['config_api_key']))); - $old_aes_key = $config_aes_key; - $config_aes_key = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['config_aes_key']))); + //$old_aes_key = $config_aes_key; + //$config_aes_key = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['config_aes_key']))); $config_base_url = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['config_base_url']))); - mysqli_query($mysqli,"UPDATE settings SET config_api_key = '$config_api_key', config_aes_key = '$config_aes_key', config_base_url = '$config_base_url' WHERE company_id = $session_company_id"); + mysqli_query($mysqli,"UPDATE settings SET config_api_key = '$config_api_key', config_base_url = '$config_base_url' WHERE company_id = $session_company_id"); - //Update AES key on client_logins if changed - if($old_aes_key != $config_aes_key){ - $sql = mysqli_query($mysqli,"SELECT login_id, AES_DECRYPT(login_password, '$old_aes_key') AS old_login_password FROM logins - WHERE company_id = $session_company_id"); - - while($row = mysqli_fetch_array($sql)){ - $login_id = $row['login_id']; - $old_login_password = $row['old_login_password']; - - mysqli_query($mysqli,"UPDATE logins SET login_password = AES_ENCRYPT('$old_login_password','$config_aes_key') WHERE login_id = $login_id"); - } - } +// //Update AES key on client_logins if changed +// if($old_aes_key != $config_aes_key){ +// $sql = mysqli_query($mysqli,"SELECT login_id, AES_DECRYPT(login_password, '$old_aes_key') AS old_login_password FROM logins +// WHERE company_id = $session_company_id"); +// +// while($row = mysqli_fetch_array($sql)){ +// $login_id = $row['login_id']; +// $old_login_password = $row['old_login_password']; +// +// mysqli_query($mysqli,"UPDATE logins SET login_password = AES_ENCRYPT('$old_login_password','$config_aes_key') WHERE login_id = $login_id"); +// } +// } //Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Settings', log_action = 'Modify', log_description = '$session_name modified general settings', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_created_at = NOW(), log_user_id = $session_user_id, company_id = $session_company_id"); @@ -948,6 +947,38 @@ if(isset($_GET['download_database'])){ $_SESSION['alert_message'] = "Database downloaded"; } +if(isset($_POST['backup_master_key'])){ + + //TODO: Verify the user is authorised to view the key? + + $password = $_POST['password']; + + $sql = mysqli_query($mysqli, "SELECT * FROM users WHERE user_id = '$session_user_id'"); + $userRow = mysqli_fetch_array($sql); + + if(password_verify($password, $userRow['user_password'])) { + $site_encryption_master_key = decryptUserSpecificKey($userRow['user_specific_encryption_ciphertext'], $password); + + //Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Settings', log_action = 'Download', log_description = '$session_name retrieved the master encryption key', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_created_at = NOW(), log_user_id = $session_user_id, company_id = $session_company_id"); + mysqli_query($mysqli,"INSERT INTO alerts SET alert_type = 'Settings', alert_message = '$session_name retrieved the master encryption key', alert_date = NOW(), company_id = $session_company_id"); + + + echo "=============================="; + echo "
Master encryption key:
"; + echo "$site_encryption_master_key"; + echo "
=============================="; + } + + else { + //Log the failure + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Settings', log_action = 'Download', log_description = '$session_name attempted to retrieve the master encryption key (failure)', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_created_at = NOW(), log_user_id = $session_user_id, company_id = $session_company_id"); + + $_SESSION['alert_message'] = "Incorrect password."; + header("Location: " . $_SERVER["HTTP_REFERER"]); + } +} + if(isset($_GET['update'])){ //also check to make sure someone has admin before running this function exec("git pull"); @@ -4126,9 +4157,9 @@ if(isset($_POST['add_asset'])){ if(!empty($_POST['username'])) { $asset_id = mysqli_insert_id($mysqli); $username = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['username']))); - $password = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['password']))); + $password = trim(mysqli_real_escape_string($mysqli,encryptLoginEntry($_POST['password']))); - mysqli_query($mysqli,"INSERT INTO logins SET login_name = '$name', login_username = '$username', login_password = AES_ENCRYPT('$password','$config_aes_key'), login_created_at = NOW(), login_asset_id = $asset_id, login_client_id = $client_id, company_id = $session_company_id"); + mysqli_query($mysqli,"INSERT INTO logins SET login_name = '$name', login_username = '$username', login_password = '$password', login_created_at = NOW(), login_asset_id = $asset_id, login_client_id = $client_id, company_id = $session_company_id"); } @@ -4251,12 +4282,12 @@ if(isset($_POST['edit_asset'])){ //If login exists then update the login if($login_id > 0){ - mysqli_query($mysqli,"UPDATE logins SET login_name = '$name', login_username = '$username', login_password = AES_ENCRYPT('$password','$config_aes_key'), login_updated_at = NOW() WHERE login_id = $login_id AND company_id = $session_company_id"); + mysqli_query($mysqli,"UPDATE logins SET login_name = '$name', login_username = '$username', login_password = '$password', login_updated_at = NOW() WHERE login_id = $login_id AND company_id = $session_company_id"); }else{ //If Username is filled in then add a login if(!empty($username)) { - mysqli_query($mysqli,"INSERT INTO logins SET login_name = '$name', login_username = '$username', login_password = AES_ENCRYPT('$password','$config_aes_key'), login_created_at = NOW(), login_asset_id = $asset_id, login_client_id = $client_id, company_id = $session_company_id"); + mysqli_query($mysqli,"INSERT INTO logins SET login_name = '$name', login_username = '$username', login_password = '$password', login_created_at = NOW(), login_asset_id = $asset_id, login_client_id = $client_id, company_id = $session_company_id"); } } @@ -4338,9 +4369,9 @@ if(isset($_POST['add_software'])){ if(!empty($_POST['username'])) { $software_id = mysqli_insert_id($mysqli); $username = strip_tags(mysqli_real_escape_string($mysqli,$_POST['username'])); - $password = strip_tags(mysqli_real_escape_string($mysqli,$_POST['password'])); + $password = trim(mysqli_real_escape_string($mysqli,encryptLoginEntry($_POST['password']))); - mysqli_query($mysqli,"INSERT INTO logins SET login_name = '$name', login_username = '$username', login_password = AES_ENCRYPT('$password','$config_aes_key'), login_software_id = $software_id, login_created_at = NOW(), login_client_id = $client_id, company_id = $session_company_id"); + mysqli_query($mysqli,"INSERT INTO logins SET login_name = '$name', login_username = '$username', login_password = '$password', login_software_id = $software_id, login_created_at = NOW(), login_client_id = $client_id, company_id = $session_company_id"); } @@ -4362,18 +4393,18 @@ if(isset($_POST['edit_software'])){ $license = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['license']))); $notes = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['notes']))); $username = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['username']))); - $password = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['password']))); + $password = trim(mysqli_real_escape_string($mysqli,encryptLoginEntry($_POST['password']))); mysqli_query($mysqli,"UPDATE software SET software_name = '$name', software_type = '$type', software_license = '$license', software_notes = '$notes', software_updated_at = NOW() WHERE software_id = $software_id AND company_id = $session_company_id"); //If login exists then update the login if($login_id > 0){ - mysqli_query($mysqli,"UPDATE logins SET login_name = '$name', login_username = '$username', login_password = AES_ENCRYPT('$password','$config_aes_key'), login_updated_at = NOW() WHERE login_id = $login_id AND company_id = $session_company_id"); + mysqli_query($mysqli,"UPDATE logins SET login_name = '$name', login_username = '$username', login_password = '$password', login_updated_at = NOW() WHERE login_id = $login_id AND company_id = $session_company_id"); }else{ //If Username is filled in then add a login if(!empty($username)) { - mysqli_query($mysqli,"INSERT INTO logins SET login_name = '$name', login_username = '$username', login_password = AES_ENCRYPT('$password','$config_aes_key'), login_created_at = NOW(), login_software_id = $software_id, login_client_id = $client_id, company_id = $session_company_id"); + mysqli_query($mysqli,"INSERT INTO logins SET login_name = '$name', login_username = '$username', login_password = '$password', login_created_at = NOW(), login_software_id = $software_id, login_client_id = $client_id, company_id = $session_company_id"); } } @@ -4513,7 +4544,7 @@ if(isset($_GET['export_client_logins_csv'])){ $client_name = $row['client_name']; - $sql = mysqli_query($mysqli,"SELECT *, AES_DECRYPT(login_password, '$config_aes_key') AS login_password FROM logins WHERE login_client_id = $client_id ORDER BY login_name ASC"); + $sql = mysqli_query($mysqli,"SELECT * FROM logins WHERE login_client_id = $client_id ORDER BY login_name ASC"); if($sql->num_rows > 0){ $delimiter = ","; $filename = $client_name . "-Logins-" . date('Y-m-d') . ".csv"; @@ -4527,7 +4558,8 @@ if(isset($_GET['export_client_logins_csv'])){ //output each row of the data, format line as csv and write to file pointer while($row = $sql->fetch_assoc()){ - $lineData = array($row['login_name'], $row['login_username'], $row['login_password'], $row['login_uri'], $row['login_note']); + $login_password = decryptLoginEntry($row['login_password']); + $lineData = array($row['login_name'], $row['login_username'], $login_password, $row['login_uri'], $row['login_note']); fputcsv($f, $lineData, $delimiter); } diff --git a/settings-backup.php b/settings-backup.php index be66aa29..601c0bde 100644 --- a/settings-backup.php +++ b/settings-backup.php @@ -4,7 +4,7 @@
-

Backup

+

Backup Database

@@ -13,4 +13,26 @@
+

+ +
+
+

Backup Master Encryption Key

+
+
+
+ +
+
+ +
+
+
+ +
+
+
+
+
+ Date: Tue, 11 Jan 2022 00:15:54 +0000 Subject: [PATCH 09/22] Update encrypt --- client_software.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client_software.php b/client_software.php index 53eb432a..313bc27b 100644 --- a/client_software.php +++ b/client_software.php @@ -39,7 +39,7 @@ if(isset($_GET['o'])){ //Rebuild URL $url_query_strings_sb = http_build_query(array_merge($_GET,array('sb' => $sb, 'o' => $o))); -$sql = mysqli_query($mysqli,"SELECT SQL_CALC_FOUND_ROWS *, AES_DECRYPT(login_password, '$config_aes_key') AS login_password FROM software LEFT JOIN logins ON login_software_id = software_id +$sql = mysqli_query($mysqli,"SELECT SQL_CALC_FOUND_ROWS * FROM software LEFT JOIN logins ON login_software_id = software_id WHERE software_client_id = $client_id AND (software_name LIKE '%$q%' OR software_type LIKE '%$q%' OR software_license LIKE '%$q%') ORDER BY $sb $o LIMIT $record_from, $record_to"); @@ -107,7 +107,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli,"SELECT FOUND_ROWS()")); $login_id = $row['login_id']; $login_username = $row['login_username']; - $login_password = $row['login_password']; + $login_password = decryptLoginEntry($row['login_password']); ?> From 3d3dc2d54d73ffc32c77fcd3e11b5f35d6f10bab Mon Sep 17 00:00:00 2001 From: Marcus Hill Date: Tue, 11 Jan 2022 00:26:09 +0000 Subject: [PATCH 10/22] Encryption updates --- client_assets.php | 4 ++-- settings-general.php | 14 -------------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/client_assets.php b/client_assets.php index cb7c389a..ef0fef63 100644 --- a/client_assets.php +++ b/client_assets.php @@ -39,7 +39,7 @@ if(isset($_GET['o'])){ //Rebuild URL $url_query_strings_sb = http_build_query(array_merge($_GET,array('sb' => $sb, 'o' => $o))); -$sql = mysqli_query($mysqli,"SELECT SQL_CALC_FOUND_ROWS *, AES_DECRYPT(login_password, '$config_aes_key') AS login_password FROM assets LEFT JOIN contacts ON asset_contact_id = contact_id LEFT JOIN locations ON asset_location_id = location_id LEFT JOIN logins ON login_asset_id = asset_id +$sql = mysqli_query($mysqli,"SELECT SQL_CALC_FOUND_ROWS * FROM assets LEFT JOIN contacts ON asset_contact_id = contact_id LEFT JOIN locations ON asset_location_id = location_id LEFT JOIN logins ON login_asset_id = asset_id WHERE asset_client_id = $client_id AND (asset_name LIKE '%$q%' OR asset_type LIKE '%$q%' OR asset_ip LIKE '%$q%' OR asset_make LIKE '%$q%' OR asset_model LIKE '%$q%' OR asset_serial LIKE '%$q%' OR asset_os LIKE '%$q%' OR contact_name LIKE '%$q%' OR location_name LIKE '%$q%') ORDER BY $sb $o LIMIT $record_from, $record_to"); @@ -178,7 +178,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli,"SELECT FOUND_ROWS()")); $login_id = $row['login_id']; $login_username = $row['login_username']; - $login_password = $row['login_password']; + $login_password = decryptLoginEntry($row['login_password']); ?> diff --git a/settings-general.php b/settings-general.php index 5acadf15..89f1ba8e 100644 --- a/settings-general.php +++ b/settings-general.php @@ -22,20 +22,6 @@ -
- -
-
- -
- -
- -
-
- This will also update the key on all client logins -
-
From bbe689fb33377496126973d116a3051aa2c6cf3b Mon Sep 17 00:00:00 2001 From: Marcus Hill Date: Tue, 11 Jan 2022 00:40:15 +0000 Subject: [PATCH 11/22] Remove comments as this is complete --- functions.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/functions.php b/functions.php index de1a80f5..67a6d31d 100644 --- a/functions.php +++ b/functions.php @@ -307,8 +307,7 @@ function setupFirstUserSpecificKey($user_password, $site_encryption_master_key){ /* For additional users / password changes New Users: Requires the admin setting up their account have the their own Specific/Session key configured -Password Changes: Will use the current info in the session. Maybe a good idea force logout users after a password change, so that upon login new session info is set fresh.. --- This logic will need to be in the pass change function itself +Password Changes: Will use the current info in the session. */ function encryptUserSpecificKey($user_password){ $iv = keygen(); From 1dd55dbfe2d4b057e3e9f0d522f4a93416ed5aa1 Mon Sep 17 00:00:00 2001 From: Marcus Hill Date: Tue, 11 Jan 2022 11:44:10 +0000 Subject: [PATCH 12/22] Add config_api_key column back for now, to avoid confusion --- db.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/db.sql b/db.sql index e1b11790..d94d0451 100644 --- a/db.sql +++ b/db.sql @@ -889,6 +889,7 @@ DROP TABLE IF EXISTS `settings`; CREATE TABLE `settings` ( `company_id` int(11) NOT NULL, `config_api_key` varchar(200) DEFAULT NULL, + `config_aes_key` varchar(250) DEFAULT NULL COMMENT 'Legacy', `config_base_url` varchar(200) DEFAULT NULL, `config_smtp_host` varchar(200) DEFAULT NULL, `config_smtp_port` int(5) DEFAULT NULL, From 951b03f712e945b724ef6df3404b45a13c0b714d Mon Sep 17 00:00:00 2001 From: Marcus Hill Date: Tue, 11 Jan 2022 14:03:34 +0000 Subject: [PATCH 13/22] Allow for encryption scheme upgrade --- functions.php | 10 +++++++ get_settings.php | 1 + login.php | 8 +++-- post.php | 72 +++++++++++++++++++++++++++++++++++++++++++++ settings-update.php | 56 +++++++++++++++++++++++++++++++++++ 5 files changed, 144 insertions(+), 3 deletions(-) diff --git a/functions.php b/functions.php index 67a6d31d..355fa732 100644 --- a/functions.php +++ b/functions.php @@ -411,6 +411,16 @@ function encryptLoginEntry($login_password_cleartext){ return $login_password_ciphertext; } +//For migrating/upgrading to the new encryption scheme +//Have to supply the master key as the cookie might not be set properly (generally requires a refresh) +function encryptUpgradeLoginEntry($login_password_cleartext, $site_encryption_master_key){ + $iv = keygen(); + //Encrypt the website/asset login using the master key + $ciphertext = openssl_encrypt($login_password_cleartext, 'aes-128-cbc', $site_encryption_master_key, 0, $iv); + + $login_password_ciphertext = $iv . $ciphertext; + return $login_password_ciphertext; +} ?> \ No newline at end of file diff --git a/get_settings.php b/get_settings.php index ba6af6ad..aae75a6d 100644 --- a/get_settings.php +++ b/get_settings.php @@ -6,6 +6,7 @@ $row = mysqli_fetch_array($sql_settings); //General $config_api_key = $row['config_api_key']; +$config_aes_key = $row['config_aes_key']; //Legacy $config_base_url = $row['config_base_url']; //Mail diff --git a/login.php b/login.php index 2ee3d646..514def06 100644 --- a/login.php +++ b/login.php @@ -46,9 +46,11 @@ if(isset($_POST['login'])){ $user_id = $row['user_id']; //Setup encryption session key - $user_encryption_ciphertext = $row['user_specific_encryption_ciphertext']; - $site_encryption_master_key = decryptUserSpecificKey($user_encryption_ciphertext, $password); - generateUserSessionKey($site_encryption_master_key); + if(isset($row['user_specific_encryption_ciphertext'])){ + $user_encryption_ciphertext = $row['user_specific_encryption_ciphertext']; + $site_encryption_master_key = decryptUserSpecificKey($user_encryption_ciphertext, $password); + generateUserSessionKey($site_encryption_master_key); + } if(empty($token)){ $_SESSION['logged'] = TRUE; diff --git a/post.php b/post.php index 3690233e..f320c5fd 100644 --- a/post.php +++ b/post.php @@ -1028,6 +1028,78 @@ if(isset($_GET['update_db'])){ header("Location: " . $_SERVER["HTTP_REFERER"]); } +if(isset($_POST['encryption_update'])){ + $password = $_POST['password']; + //$session_company_id + + //Get user details + $sql = mysqli_query($mysqli,"SELECT * FROM users WHERE user_id = '$session_user_id'"); + $row = mysqli_fetch_array($sql); + + //Verify the users password + if(!password_verify($password, $row['user_password'])){ + echo "Password incorrect."; + exit(); + } + + //First, check if this user is setup for the new encryption setup + if(isset($row['user_specific_encryption_ciphertext'])){ + echo "Ciphertext data already exists, using it.
"; + $user_encryption_ciphertext = $row['user_specific_encryption_ciphertext']; + $site_encryption_master_key = decryptUserSpecificKey($user_encryption_ciphertext, $password); + } + else{ + echo "Ciphertext data not found, attempting to adding it."; + $update_table = mysqli_query($mysqli, "ALTER TABLE `users` ADD `user_specific_encryption_ciphertext` VARCHAR(200) NULL AFTER `user_avatar`; "); + + if(!$update_table){ + echo "Error adding ciphertext column to users table. Either there was a connection/permissions issue or the column already exists due to a upgrade already taking place?
"; + exit(); + } + + echo "Ciphertext column added successfully!
"; + + echo "Generating new master key.
"; + $site_encryption_master_key = keygen(); + echo "New master key is: $site_encryption_master_key
"; + $user_encryption_ciphertext = setupFirstUserSpecificKey($password, $site_encryption_master_key); + + $set_user_specific_key = mysqli_query($mysqli, "UPDATE users SET user_specific_encryption_ciphertext = '$user_encryption_ciphertext' user_id = '$session_user_id'"); + if(!$set_user_specific_key){ + echo "Something went wrong adding your user specific key.
"; + exit(); + } + + //Setup the user session key + generateUserSessionKey($site_encryption_master_key); + + //Invalidate user passwords + //If we don't do this, users won't be able to see the new passwords properly, and could potentially add passwords that can never be decrypted + mysqli_query($mysqli, "UPDATE users SET login_password = 'Invalid due to upgrade'"); + $extended_log_description = ", invalidated all user passwords"; + echo "Invalidated all user passwords. You must re-set them from this user.
"; + } + + //Either way, if we got here we now have the master key as $site_encryption_master_key + + //Get & upgrade user login encryption + $sql_logins = mysqli_query($mysqli,"SELECT *, AES_DECRYPT(login_password, '$config_aes_key') AS login_password FROM logins WHERE (company_id = '$session_company_id' AND login_password IS NOT NULL)"); + $count = 0; + foreach ($sql_logins as $row){ + $login_id = $row['login_id']; + $new_encrypted_password = encryptUpgradeLoginEntry($row['login_password'], $site_encryption_master_key); + mysqli_query($mysqli, "UPDATE logins SET login_password = '$new_encrypted_password' WHERE login_id = '$login_id'"); + $count++; + } + echo "Upgraded $count records.
"; + + //Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Settings', log_action = 'Migrate', log_description = '$session_name upgraded $session_company_id logins to the new encryption scheme$extended_log_description', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_created_at = NOW(), log_user_id = $session_user_id, company_id = $session_company_id"); + + echo "Migration for company successful."; + +} + if(isset($_POST['add_client'])){ $name = trim(strip_tags(mysqli_real_escape_string($mysqli,$_POST['name']))); diff --git a/settings-update.php b/settings-update.php index b0f54a20..36b1e00d 100644 --- a/settings-update.php +++ b/settings-update.php @@ -61,4 +61,60 @@ $git_log = shell_exec("git log master..origin/master --pretty=format:'%h
+ +
+
+

Update AES Key

+
+
+
+
+ +
+"; +echo "Current User ID: $session_user_id
"; + +if ($config_aes_key) { + echo "Current AES key: $config_aes_key

"; + echo "Below are the decrypted credentials for five login entries, please confirm they show and are correct before continuing.
Do NOT continue if no entries are shown or if the decrypted passwords are incorrect.

"; + $sql = mysqli_query($mysqli,"SELECT *, AES_DECRYPT(login_password, '$config_aes_key') AS login_password FROM logins WHERE (company_id = '$session_company_id' AND login_password IS NOT NULL) LIMIT 5"); + foreach ($sql as $row){ + echo $row['login_username'] . ":" . $row['login_password']; + echo "
"; + } + echo "
"; + ?> + +
+
+
+ +
+
+ +
+
+"; + echo "Please ensure upgrade is required. If you are sure you need to update, ensure the AES key is for this company."; +} + +?> +
+
+
+ Date: Tue, 11 Jan 2022 18:22:45 +0000 Subject: [PATCH 14/22] r --- post.php | 19 +++++++++++-------- settings-update.php | 4 ++-- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/post.php b/post.php index f320c5fd..03f8bf30 100644 --- a/post.php +++ b/post.php @@ -1030,7 +1030,6 @@ if(isset($_GET['update_db'])){ if(isset($_POST['encryption_update'])){ $password = $_POST['password']; - //$session_company_id //Get user details $sql = mysqli_query($mysqli,"SELECT * FROM users WHERE user_id = '$session_user_id'"); @@ -1038,8 +1037,8 @@ if(isset($_POST['encryption_update'])){ //Verify the users password if(!password_verify($password, $row['user_password'])){ - echo "Password incorrect."; - exit(); + $_SESSION['alert_message'] = "User password incorrect."; + header("Location: " . $_SERVER["HTTP_REFERER"]); } //First, check if this user is setup for the new encryption setup @@ -1049,11 +1048,12 @@ if(isset($_POST['encryption_update'])){ $site_encryption_master_key = decryptUserSpecificKey($user_encryption_ciphertext, $password); } else{ - echo "Ciphertext data not found, attempting to adding it."; + echo "User ciphertext data not found, attempting to add it.
"; $update_table = mysqli_query($mysqli, "ALTER TABLE `users` ADD `user_specific_encryption_ciphertext` VARCHAR(200) NULL AFTER `user_avatar`; "); if(!$update_table){ - echo "Error adding ciphertext column to users table. Either there was a connection/permissions issue or the column already exists due to a upgrade already taking place?
"; + echo "Error adding ciphertext column (user_specific_encryption_ciphertext) to users table."; + echo "Either there was a connection/permissions issue or the column already exists due to a upgrade already taking place?
"; exit(); } @@ -1064,7 +1064,7 @@ if(isset($_POST['encryption_update'])){ echo "New master key is: $site_encryption_master_key
"; $user_encryption_ciphertext = setupFirstUserSpecificKey($password, $site_encryption_master_key); - $set_user_specific_key = mysqli_query($mysqli, "UPDATE users SET user_specific_encryption_ciphertext = '$user_encryption_ciphertext' user_id = '$session_user_id'"); + $set_user_specific_key = mysqli_query($mysqli, "UPDATE users SET user_specific_encryption_ciphertext = '$user_encryption_ciphertext' WHERE user_id = '$session_user_id'"); if(!$set_user_specific_key){ echo "Something went wrong adding your user specific key.
"; exit(); @@ -1077,7 +1077,7 @@ if(isset($_POST['encryption_update'])){ //If we don't do this, users won't be able to see the new passwords properly, and could potentially add passwords that can never be decrypted mysqli_query($mysqli, "UPDATE users SET login_password = 'Invalid due to upgrade'"); $extended_log_description = ", invalidated all user passwords"; - echo "Invalidated all user passwords. You must re-set them from this user.
"; + echo "Invalidated all user passwords. You must re-set them from this user account.
"; } //Either way, if we got here we now have the master key as $site_encryption_master_key @@ -1096,7 +1096,10 @@ if(isset($_POST['encryption_update'])){ //Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Settings', log_action = 'Migrate', log_description = '$session_name upgraded $session_company_id logins to the new encryption scheme$extended_log_description', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_created_at = NOW(), log_user_id = $session_user_id, company_id = $session_company_id"); - echo "Migration for company successful."; + echo "Migration for company successful.
"; + $_SESSION['alert_message'] = "Migration for company successful."; + + echo "Back to settings."; } diff --git a/settings-update.php b/settings-update.php index 36b1e00d..a386cef2 100644 --- a/settings-update.php +++ b/settings-update.php @@ -70,7 +70,7 @@ $git_log = shell_exec("git log master..origin/master --pretty=format:'%h

+

Warning: This action is irreversible. Do NOT proceed without a backup.

@@ -109,7 +110,7 @@ if ($config_aes_key) { } else { echo "Config AES key is not set for this company.
"; - echo "Please ensure upgrade is required. If you are sure you need to update, ensure the AES key is for this company."; + echo "Please ensure upgrade is required. If you are sure you need to update, ensure the AES key is set correctly for this company."; } ?> From 82107679ccf737e76b74b6391577eb0252cb3104 Mon Sep 17 00:00:00 2001 From: Marcus Hill Date: Tue, 11 Jan 2022 19:16:49 +0000 Subject: [PATCH 18/22] More. --- post.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/post.php b/post.php index f8f7bb33..41fb4a8f 100644 --- a/post.php +++ b/post.php @@ -1077,7 +1077,7 @@ if(isset($_POST['encryption_update'])){ //Invalidate user passwords //If we don't do this, users won't be able to see the new passwords properly, and could potentially add passwords that can never be decrypted - mysqli_query($mysqli, "UPDATE users SET user_password = 'Invalid due to upgrade'"); + mysqli_query($mysqli, "UPDATE users SET user_password = 'Invalid due to upgrade' WHERE user_id NOT IN ($userion_user_id)"); $extended_log_description = ", invalidated all user passwords"; echo "Invalidated all user passwords. You must re-set them from this user account.
"; } From fccae5dedbe8f6a54986888b037f4fc9673317b7 Mon Sep 17 00:00:00 2001 From: Marcus Hill Date: Tue, 11 Jan 2022 19:22:06 +0000 Subject: [PATCH 19/22] Typo in user id variable --- post.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/post.php b/post.php index 41fb4a8f..eb319e26 100644 --- a/post.php +++ b/post.php @@ -1077,7 +1077,7 @@ if(isset($_POST['encryption_update'])){ //Invalidate user passwords //If we don't do this, users won't be able to see the new passwords properly, and could potentially add passwords that can never be decrypted - mysqli_query($mysqli, "UPDATE users SET user_password = 'Invalid due to upgrade' WHERE user_id NOT IN ($userion_user_id)"); + mysqli_query($mysqli, "UPDATE users SET user_password = 'Invalid due to upgrade' WHERE user_id NOT IN ($session_user_id)"); $extended_log_description = ", invalidated all user passwords"; echo "Invalidated all user passwords. You must re-set them from this user account.
"; } From 2742410e4b05b163e7d18e2c0ecb0323fdbafd3a Mon Sep 17 00:00:00 2001 From: Marcus Hill Date: Tue, 11 Jan 2022 19:44:21 +0000 Subject: [PATCH 20/22] http/https encryption cookie --- functions.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/functions.php b/functions.php index 355fa732..f664fde4 100644 --- a/functions.php +++ b/functions.php @@ -368,7 +368,13 @@ function generateUserSessionKey($site_encryption_master_key){ $_SESSION['user_encryption_session_iv'] = $user_encryption_session_iv; //Give the user "their" key as a cookie - setcookie("user_encryption_session_key", $user_encryption_session_key, 0, "/", "", "true", "true"); + if($config_https_only){ + setcookie("user_encryption_session_key", $user_encryption_session_key, 0, "/", "", "true", "true"); + } + else { + // No secure flag + setcookie("user_encryption_session_key", $user_encryption_session_key, 0, "/", "", "false", "true"); + } } //Decrypts an encrypted password (website/asset login), returns it as a string From 0382dbbfb2c680245cf00045444816bee795f022 Mon Sep 17 00:00:00 2001 From: Marcus Hill Date: Tue, 11 Jan 2022 20:14:29 +0000 Subject: [PATCH 21/22] Small change re https --- functions.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/functions.php b/functions.php index f664fde4..2be719db 100644 --- a/functions.php +++ b/functions.php @@ -368,12 +368,13 @@ function generateUserSessionKey($site_encryption_master_key){ $_SESSION['user_encryption_session_iv'] = $user_encryption_session_iv; //Give the user "their" key as a cookie + //By default, this should be HTTPS but we can change to HTTP for development via the config.php file if($config_https_only){ setcookie("user_encryption_session_key", $user_encryption_session_key, 0, "/", "", "true", "true"); } - else { - // No secure flag - setcookie("user_encryption_session_key", $user_encryption_session_key, 0, "/", "", "false", "true"); + else{ + setcookie("user_encryption_session_key", $user_encryption_session_key, 0, "/"); + $_SESSION['alert_message'] = "Unencrypted connection: Using HTTP only."; } } From 2a4d42de093cbf8f8dd55145c2527ecf2d9854b1 Mon Sep 17 00:00:00 2001 From: Marcus Hill Date: Tue, 11 Jan 2022 20:42:46 +0000 Subject: [PATCH 22/22] Encryption changes --- functions.php | 3 ++- post.php | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/functions.php b/functions.php index 2be719db..e38746b1 100644 --- a/functions.php +++ b/functions.php @@ -369,12 +369,13 @@ function generateUserSessionKey($site_encryption_master_key){ //Give the user "their" key as a cookie //By default, this should be HTTPS but we can change to HTTP for development via the config.php file + include('config.php'); if($config_https_only){ setcookie("user_encryption_session_key", $user_encryption_session_key, 0, "/", "", "true", "true"); } else{ setcookie("user_encryption_session_key", $user_encryption_session_key, 0, "/"); - $_SESSION['alert_message'] = "Unencrypted connection: Using HTTP only."; + $_SESSION['alert_message'] = "Unencrypted connection flag set: Using non-secure cookies."; } } diff --git a/post.php b/post.php index eb319e26..bcf9459d 100644 --- a/post.php +++ b/post.php @@ -6565,11 +6565,12 @@ if(isset($_GET['logout'])){ mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Logout', log_action = 'Success', log_description = '$session_name logged out', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_created_at = NOW(), log_user_id = $session_user_id"); session_start(); - session_destroy(); + setcookie("user_encryption_session_key", '', time() - 3600, "/"); unset($_COOKIE['user_encryption_session_key']); - setcookie("user_encryption_session_key", '', time() - 3600, "/", "", "true", "true"); + session_unset(); + session_destroy(); header('Location: login.php'); }