diff --git a/ajax.php b/ajax.php
index 33b2ee7a..9dab8381 100644
--- a/ajax.php
+++ b/ajax.php
@@ -230,7 +230,7 @@ if (isset($_GET['share_generate_link'])) {
$item_note = trim(strip_tags(mysqli_real_escape_string($mysqli,$_GET['note'])));
$item_view_limit = intval($_GET['views']);
$item_expires = trim(strip_tags(mysqli_real_escape_string($mysqli,$_GET['expires'])));
- $item_key = bin2hex(random_bytes(78));
+ $item_key = randomString(156);
if ($item_type == "Document") {
$row = mysqli_fetch_array(mysqli_query($mysqli, "SELECT document_name FROM documents WHERE document_id = '$item_id' AND document_client_id = '$client_id' LIMIT 1"));
@@ -250,8 +250,8 @@ if (isset($_GET['share_generate_link'])) {
// Decrypt & re-encrypt password for sharing
$login_password_cleartext = decryptLoginEntry($row['login_password']);
- $login_encryption_key = bin2hex(random_bytes(8));
- $iv = bin2hex(random_bytes(8));
+ $login_encryption_key = randomString();
+ $iv = randomString();
$ciphertext = openssl_encrypt($login_password_cleartext, 'aes-128-cbc', $login_encryption_key, 0, $iv);
$item_encrypted_credential = $iv . $ciphertext;
diff --git a/api_key_add_modal.php b/api_key_add_modal.php
index 0c74dcbe..8b839759 100644
--- a/api_key_add_modal.php
+++ b/api_key_add_modal.php
@@ -1,5 +1,5 @@
diff --git a/blank.php b/blank.php
index fd2a1c16..59f5fa50 100644
--- a/blank.php
+++ b/blank.php
@@ -16,9 +16,8 @@
-
+
-
diff --git a/cron.php b/cron.php
index 87f90f4b..19368f86 100644
--- a/cron.php
+++ b/cron.php
@@ -325,7 +325,7 @@ while($row = mysqli_fetch_array($sql_companies)){
mysqli_query($mysqli,"UPDATE settings SET config_invoice_next_number = $new_config_invoice_next_number WHERE company_id = $company_id");
//Generate a unique URL key for clients to access
- $url_key = bin2hex(random_bytes(78));
+ $url_key = randomString(156);
mysqli_query($mysqli,"INSERT INTO invoices SET invoice_prefix = '$config_invoice_prefix', invoice_number = $new_invoice_number, invoice_scope = '$recurring_scope', invoice_date = CURDATE(), invoice_due = DATE_ADD(CURDATE(), INTERVAL $client_net_terms day), invoice_amount = '$recurring_amount', invoice_currency_code = '$recurring_currency_code', invoice_note = '$recurring_note', invoice_category_id = $category_id, invoice_status = 'Sent', invoice_url_key = '$url_key', invoice_created_at = NOW(), invoice_client_id = $client_id, company_id = $company_id");
@@ -408,4 +408,4 @@ while($row = mysqli_fetch_array($sql_companies)){
} //End Company Loop through
-?>
\ No newline at end of file
+?>
diff --git a/functions.php b/functions.php
index 639a4f42..a17a8fcc 100644
--- a/functions.php
+++ b/functions.php
@@ -11,22 +11,25 @@ require_once("plugins/PHPMailer/src/SMTP.php");
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
-function keygen()
+// Function to generate both crypto & URL safe random strings
+function randomString($length = 16)
{
- $chars = "abcdefghijklmnopqrstuvwxyz";
- $chars .= "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
- $chars .= "0123456789";
- while (1) {
- $key = '';
- srand((double) microtime() * 1000000);
- for ($i = 0; $i < 16; $i++) {
- $key .= substr($chars, (rand() % (strlen($chars))), 1);
- }
- break;
- }
- return $key;
+ // Generate some cryptographically safe random bytes
+ // Generate a little more than requested as we'll lose some later converting
+ $random_bytes = random_bytes($length + 5);
+
+ // Convert the bytes to something somewhat human-readable
+ $random_base_64 = base64_encode($random_bytes);
+
+ // Replace the nasty characters that come with base64
+ $bad_chars = array("/", "+", "=");
+ $random_string = str_replace($bad_chars, random_int(0, 9), $random_base_64);
+
+ // Truncate the string to the requested $length and return
+ return substr($random_string, 0, $length);
}
+// Older keygen function - only used for TOTP currently
function key32gen()
{
$chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
@@ -226,8 +229,8 @@ 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 = bin2hex(random_bytes(8));
- $salt = bin2hex(random_bytes(8));
+ $iv = randomString();
+ $salt = randomString();
//Generate 128-bit (16 byte/char) kdhash of the users password
$user_password_kdhash = hash_pbkdf2('sha256', $user_password, $salt, 100000, 16);
@@ -243,9 +246,10 @@ function setupFirstUserSpecificKey($user_password, $site_encryption_master_key)
* New Users: Requires the admin setting up their account have a Specific/Session key configured
* Password Changes: Will use the current info in the session.
*/
-function encryptUserSpecificKey($user_password) {
- $iv = bin2hex(random_bytes(8));
- $salt = bin2hex(random_bytes(8));
+function encryptUserSpecificKey($user_password)
+{
+ $iv = randomString();
+ $salt = randomString();
// Get the session info.
$user_encryption_session_ciphertext = $_SESSION['user_encryption_session_ciphertext'];
@@ -267,7 +271,8 @@ function encryptUserSpecificKey($user_password) {
// 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) {
+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);
@@ -287,11 +292,10 @@ Generates what is probably best described as a session key (ephemeral-ish)
- 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 bin2hex(random_bytes(8))
- $user_encryption_session_key = bin2hex(random_bytes(8));
- $user_encryption_session_iv = bin2hex(random_bytes(8));
+function generateUserSessionKey($site_encryption_master_key)
+{
+ $user_encryption_session_key = randomString();
+ $user_encryption_session_iv = randomString();
$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
@@ -309,7 +313,8 @@ function generateUserSessionKey($site_encryption_master_key) {
}
// Decrypts an encrypted password (website/asset login), returns it as a string
-function decryptLoginEntry($login_password_ciphertext) {
+function decryptLoginEntry($login_password_ciphertext)
+{
// Split the login into IV and Ciphertext
$login_iv = substr($login_password_ciphertext, 0, 16);
@@ -329,8 +334,9 @@ function decryptLoginEntry($login_password_ciphertext) {
}
// Encrypts a website/asset login password
-function encryptLoginEntry($login_password_cleartext) {
- $iv = bin2hex(random_bytes(8));
+function encryptLoginEntry($login_password_cleartext)
+{
+ $iv = randomString();
// Get the user session info.
$user_encryption_session_ciphertext = $_SESSION['user_encryption_session_ciphertext'];
diff --git a/login.php b/login.php
index c9daf9a7..2d5a7e3e 100644
--- a/login.php
+++ b/login.php
@@ -117,7 +117,7 @@ if (isset($_POST['login'])) {
$_SESSION['user_id'] = $user_id;
$_SESSION['user_name'] = $user_name;
$_SESSION['user_role'] = $row['user_role'];
- $_SESSION['csrf_token'] = bin2hex(random_bytes(78));
+ $_SESSION['csrf_token'] = randomString(156);
$_SESSION['logged'] = TRUE;
// Setup encryption session key
diff --git a/portal/login_reset.php b/portal/login_reset.php
index e9cf0888..664a7ef7 100644
--- a/portal/login_reset.php
+++ b/portal/login_reset.php
@@ -51,7 +51,7 @@ if ($_SERVER['REQUEST_METHOD'] == "POST") {
$company = $row['company_id'];
if ($row['contact_email'] == $email) {
- $token = bin2hex(random_bytes(78));
+ $token = randomString(156);
$url = "https://$config_base_url/portal/login_reset.php?email=$email&token=$token&client=$client";
mysqli_query($mysqli, "UPDATE contacts SET contact_password_reset_token = '$token' WHERE contact_id = $id LIMIT 1");
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Contact', log_action = 'Modify', log_description = 'Sent a portal password reset e-mail for $email.', log_ip = '$ip', log_user_agent = '$user_agent', log_created_at = NOW(), log_client_id = $client, company_id = $company");
diff --git a/post.php b/post.php
index 51621d29..d39fadd0 100644
--- a/post.php
+++ b/post.php
@@ -373,7 +373,7 @@ if(isset($_POST['edit_profile'])){
// Enable extension access, only if it isn't already setup (user doesn't have cookie)
if(isset($_POST['extension']) && $_POST['extension'] == 'Yes'){
if(!isset($_COOKIE['user_extension_key'])){
- $extension_key = bin2hex(random_bytes(78));
+ $extension_key = randomString(156);
mysqli_query($mysqli, "UPDATE users SET user_extension_key = '$extension_key' WHERE user_id = $user_id");
$extended_log_description .= ", extension access enabled";
@@ -2878,7 +2878,7 @@ if(isset($_POST['add_invoice'])){
mysqli_query($mysqli,"UPDATE settings SET config_invoice_next_number = $new_config_invoice_next_number WHERE company_id = $session_company_id");
//Generate a unique URL key for clients to access
- $url_key = bin2hex(random_bytes(78));
+ $url_key = randomString(156);
mysqli_query($mysqli,"INSERT INTO invoices SET invoice_prefix = '$config_invoice_prefix', invoice_number = $invoice_number, invoice_scope = '$scope', invoice_date = '$date', invoice_due = DATE_ADD('$date', INTERVAL $client_net_terms day), invoice_currency_code = '$session_company_currency', invoice_category_id = $category, invoice_status = 'Draft', invoice_url_key = '$url_key', invoice_client_id = $client, company_id = $session_company_id");
$invoice_id = mysqli_insert_id($mysqli);
@@ -2936,7 +2936,7 @@ if(isset($_POST['add_invoice_copy'])){
$category_id = $row['invoice_category_id'];
//Generate a unique URL key for clients to access
- $url_key = bin2hex(random_bytes(78));
+ $url_key = randomString(156);
mysqli_query($mysqli,"INSERT INTO invoices SET invoice_prefix = '$config_invoice_prefix', invoice_number = $invoice_number, invoice_scope = '$invoice_scope', invoice_date = '$date', invoice_due = DATE_ADD('$date', INTERVAL $client_net_terms day), invoice_category_id = $category_id, invoice_status = 'Draft', invoice_amount = '$invoice_amount', invoice_currency_code = '$invoice_currency_code', invoice_note = '$invoice_note', invoice_url_key = '$url_key', invoice_client_id = $client_id, company_id = $session_company_id") or die(mysql_error());
@@ -3031,7 +3031,7 @@ if(isset($_POST['add_quote'])){
mysqli_query($mysqli,"UPDATE settings SET config_quote_next_number = $new_config_quote_next_number WHERE company_id = $session_company_id");
//Generate a unique URL key for clients to access
- $quote_url_key = bin2hex(random_bytes(78));
+ $quote_url_key = randomString(156);
mysqli_query($mysqli,"INSERT INTO quotes SET quote_prefix = '$config_quote_prefix', quote_number = $quote_number, quote_scope = '$scope', quote_date = '$date', quote_currency_code = '$session_company_currency', quote_category_id = $category, quote_status = 'Draft', quote_url_key = '$quote_url_key', quote_client_id = $client, company_id = $session_company_id");
@@ -3068,7 +3068,7 @@ if(isset($_POST['add_quote_copy'])){
$category_id = $row['quote_category_id'];
//Generate a unique URL key for clients to access
- $quote_url_key = bin2hex(random_bytes(78));
+ $quote_url_key = randomString(156);
mysqli_query($mysqli,"INSERT INTO quotes SET quote_prefix = '$config_quote_prefix', quote_number = $quote_number, quote_scope = '$quote_scope', quote_date = '$date', quote_category_id = $category_id, quote_status = 'Draft', quote_amount = '$quote_amount', quote_currency_code = '$quote_currency_code', quote_note = '$quote_note', quote_url_key = '$quote_url_key', quote_client_id = $client_id, company_id = $session_company_id");
@@ -3121,7 +3121,7 @@ if(isset($_POST['add_quote_to_invoice'])){
$category_id = $row['quote_category_id'];
//Generate a unique URL key for clients to access
- $url_key = bin2hex(random_bytes(78));
+ $url_key = randomString(156);
mysqli_query($mysqli,"INSERT INTO invoices SET invoice_prefix = '$config_invoice_prefix', invoice_number = $invoice_number, invoice_scope = '$quote_scope', invoice_date = '$date', invoice_due = DATE_ADD(CURDATE(), INTERVAL $client_net_terms day), invoice_category_id = $category_id, invoice_status = 'Draft', invoice_amount = '$quote_amount', invoice_currency_code = '$quote_currency_code', invoice_note = '$quote_note', invoice_url_key = '$url_key', invoice_client_id = $client_id, company_id = $session_company_id");
@@ -6733,7 +6733,7 @@ if(isset($_POST['add_invoice_from_ticket'])){
mysqli_query($mysqli,"UPDATE settings SET config_invoice_next_number = $new_config_invoice_next_number WHERE company_id = $session_company_id");
//Generate a unique URL key for clients to access
- $url_key = bin2hex(random_bytes(78));
+ $url_key = randomString(156);
mysqli_query($mysqli,"INSERT INTO invoices SET invoice_prefix = '$config_invoice_prefix', invoice_number = $invoice_number, invoice_scope = '$scope', invoice_date = '$date', invoice_due = DATE_ADD('$date', INTERVAL $client_net_terms day), invoice_currency_code = '$session_company_currency', invoice_category_id = $category, invoice_status = 'Draft', invoice_url_key = '$url_key', invoice_client_id = $client_id, company_id = $session_company_id");
$invoice_id = mysqli_insert_id($mysqli);
@@ -7443,7 +7443,7 @@ if(isset($_GET['force_recurring'])){
mysqli_query($mysqli,"UPDATE settings SET config_invoice_next_number = $new_config_invoice_next_number WHERE company_id = $session_company_id");
//Generate a unique URL key for clients to access
- $url_key = bin2hex(random_bytes(78));
+ $url_key = randomString(156);
mysqli_query($mysqli,"INSERT INTO invoices SET invoice_prefix = '$config_invoice_prefix', invoice_number = '$new_invoice_number', invoice_scope = '$recurring_scope', invoice_date = CURDATE(), invoice_due = DATE_ADD(CURDATE(), INTERVAL $client_net_terms day), invoice_amount = '$recurring_amount', invoice_currency_code = '$recurring_currency_code', invoice_note = '$recurring_note', invoice_category_id = $category_id, invoice_status = 'Sent', invoice_url_key = '$url_key', invoice_client_id = $client_id, company_id = $session_company_id");
diff --git a/setup.php b/setup.php
index 43e7d8dd..9d08bfc2 100644
--- a/setup.php
+++ b/setup.php
@@ -848,7 +848,7 @@ if (isset($_POST['add_user'])) {
$password = password_hash($_POST['password'], PASSWORD_DEFAULT);
//Generate master encryption key
- $site_encryption_master_key = bin2hex(random_bytes(8));
+ $site_encryption_master_key = randomString();
//Generate user specific key
$user_specific_encryption_ciphertext = setupFirstUserSpecificKey($_POST['password'], $site_encryption_master_key);
diff --git a/users.php b/users.php
index fc30f7e2..10e53d7c 100644
--- a/users.php
+++ b/users.php
@@ -170,7 +170,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));