From 858d3386b75d430d8cad839f59bacc46ebca420a Mon Sep 17 00:00:00 2001
From: Jesse Norell <jesse@kci.net>
Date: Tue, 1 Feb 2022 16:58:39 -0700
Subject: [PATCH] more secure unique strings

---
 .../autoinstall.conf_sample.php               |  4 +-
 install/dist/conf/centos80.conf.php           |  4 +-
 install/dist/conf/debian100.conf.php          |  4 +-
 install/dist/conf/debian110.conf.php          |  4 +-
 install/dist/conf/debian90.conf.php           |  4 +-
 install/dist/conf/debiantesting.conf.php      |  4 +-
 install/dist/conf/gentoo.conf.php             |  4 +-
 install/dist/conf/ubuntu1604.conf.php         |  4 +-
 install/dist/conf/ubuntu1710.conf.php         |  4 +-
 install/dist/conf/ubuntu1804.conf.php         |  4 +-
 install/dist/conf/ubuntu2004.conf.php         |  4 +-
 install/lib/installer_base.lib.php            |  1 +
 interface/lib/app.inc.php                     |  2 +
 interface/lib/classes/auth.inc.php            | 18 ++---
 interface/lib/classes/functions.inc.php       | 18 ++---
 interface/lib/classes/remoting.inc.php        |  4 +-
 interface/lib/compatibility.inc.php           | 80 +++++++++++++++++++
 interface/web/login/password_reset.php        |  2 +-
 server/lib/classes/functions.inc.php          |  6 +-
 server/lib/classes/letsencrypt.inc.php        |  2 +-
 .../plugins-available/apache2_plugin.inc.php  | 11 +--
 server/plugins-available/nginx_plugin.inc.php | 12 +--
 22 files changed, 136 insertions(+), 64 deletions(-)
 create mode 100644 interface/lib/compatibility.inc.php

diff --git a/docs/autoinstall_samples/autoinstall.conf_sample.php b/docs/autoinstall_samples/autoinstall.conf_sample.php
index 904d65403c..c8bf209f9f 100644
--- a/docs/autoinstall_samples/autoinstall.conf_sample.php
+++ b/docs/autoinstall_samples/autoinstall.conf_sample.php
@@ -29,7 +29,7 @@ $autoinstall['ssl_cert_email'] = 'hostmaster@'.$autoinstall['hostname'];
 
 /* optional expert mode settings, needed only for expert mode */
 $autoinstall['mysql_ispconfig_user'] = 'ispconfig'; // default: ispconfig
-$autoinstall['mysql_ispconfig_password'] = md5(uniqid(rand()));
+$autoinstall['mysql_ispconfig_password'] = bin2hex(random_bytes(20));
 $autoinstall['join_multiserver_setup'] = 'n'; // y, n (default)
 $autoinstall['mysql_master_hostname'] = 'master.example.com';
 $autoinstall['mysql_master_root_user'] = 'root';
@@ -70,4 +70,4 @@ $autoupdate['svc_detect_change_firewall_server'] = 'yes'; // yes (default), no
 $autoupdate['svc_detect_change_vserver_server'] = 'yes'; // yes (default), no
 $autoupdate['svc_detect_change_db_server'] = 'yes'; // yes (default), no
 
-?>
\ No newline at end of file
+?>
diff --git a/install/dist/conf/centos80.conf.php b/install/dist/conf/centos80.conf.php
index 0411fb9ce5..36e85e02d2 100644
--- a/install/dist/conf/centos80.conf.php
+++ b/install/dist/conf/centos80.conf.php
@@ -63,14 +63,14 @@ $conf['mysql']['admin_user'] = 'root';
 $conf['mysql']['admin_password'] = '';
 $conf['mysql']['charset'] = 'utf8';
 $conf['mysql']['ispconfig_user'] = 'ispconfig';
-$conf['mysql']['ispconfig_password'] = md5(uniqid(rand()));
+$conf['mysql']['ispconfig_password'] = md5(random_bytes(20));
 $conf['mysql']['master_slave_setup'] = 'n';
 $conf['mysql']['master_host'] = '';
 $conf['mysql']['master_database'] = 'dbispconfig';
 $conf['mysql']['master_admin_user'] = 'root';
 $conf['mysql']['master_admin_password'] = '';
 $conf['mysql']['master_ispconfig_user'] = '';
-$conf['mysql']['master_ispconfig_password'] = md5(uniqid(rand()));
+$conf['mysql']['master_ispconfig_password'] = md5(random_bytes(20));
 
 //* Apache
 $conf['apache']['installed'] = false; // will be detected automatically during installation
diff --git a/install/dist/conf/debian100.conf.php b/install/dist/conf/debian100.conf.php
index 0861af83de..b6b0dc4135 100644
--- a/install/dist/conf/debian100.conf.php
+++ b/install/dist/conf/debian100.conf.php
@@ -65,14 +65,14 @@ $conf['mysql']['admin_user'] = 'root';
 $conf['mysql']['admin_password'] = '';
 $conf['mysql']['charset'] = 'utf8';
 $conf['mysql']['ispconfig_user'] = 'ispconfig';
-$conf['mysql']['ispconfig_password'] = md5(uniqid(rand()));
+$conf['mysql']['ispconfig_password'] = md5(random_bytes(20));
 $conf['mysql']['master_slave_setup'] = 'n';
 $conf['mysql']['master_host'] = '';
 $conf['mysql']['master_database'] = 'dbispconfig';
 $conf['mysql']['master_admin_user'] = 'root';
 $conf['mysql']['master_admin_password'] = '';
 $conf['mysql']['master_ispconfig_user'] = '';
-$conf['mysql']['master_ispconfig_password'] = md5(uniqid(rand()));
+$conf['mysql']['master_ispconfig_password'] = md5(random_bytes(20));
 
 //* Apache
 $conf['apache']['installed'] = false; // will be detected automatically during installation
diff --git a/install/dist/conf/debian110.conf.php b/install/dist/conf/debian110.conf.php
index c60152df5c..10f57d88a1 100644
--- a/install/dist/conf/debian110.conf.php
+++ b/install/dist/conf/debian110.conf.php
@@ -65,14 +65,14 @@ $conf['mysql']['admin_user'] = 'root';
 $conf['mysql']['admin_password'] = '';
 $conf['mysql']['charset'] = 'utf8';
 $conf['mysql']['ispconfig_user'] = 'ispconfig';
-$conf['mysql']['ispconfig_password'] = md5(uniqid(rand()));
+$conf['mysql']['ispconfig_password'] = md5(random_bytes(20));
 $conf['mysql']['master_slave_setup'] = 'n';
 $conf['mysql']['master_host'] = '';
 $conf['mysql']['master_database'] = 'dbispconfig';
 $conf['mysql']['master_admin_user'] = 'root';
 $conf['mysql']['master_admin_password'] = '';
 $conf['mysql']['master_ispconfig_user'] = '';
-$conf['mysql']['master_ispconfig_password'] = md5(uniqid(rand()));
+$conf['mysql']['master_ispconfig_password'] = md5(random_bytes(20));
 
 //* Apache
 $conf['apache']['installed'] = false; // will be detected automatically during installation
diff --git a/install/dist/conf/debian90.conf.php b/install/dist/conf/debian90.conf.php
index e5d1d8a9b4..b253a31f22 100644
--- a/install/dist/conf/debian90.conf.php
+++ b/install/dist/conf/debian90.conf.php
@@ -65,14 +65,14 @@ $conf['mysql']['admin_user'] = 'root';
 $conf['mysql']['admin_password'] = '';
 $conf['mysql']['charset'] = 'utf8';
 $conf['mysql']['ispconfig_user'] = 'ispconfig';
-$conf['mysql']['ispconfig_password'] = md5(uniqid(rand()));
+$conf['mysql']['ispconfig_password'] = md5(random_bytes(20));
 $conf['mysql']['master_slave_setup'] = 'n';
 $conf['mysql']['master_host'] = '';
 $conf['mysql']['master_database'] = 'dbispconfig';
 $conf['mysql']['master_admin_user'] = 'root';
 $conf['mysql']['master_admin_password'] = '';
 $conf['mysql']['master_ispconfig_user'] = '';
-$conf['mysql']['master_ispconfig_password'] = md5(uniqid(rand()));
+$conf['mysql']['master_ispconfig_password'] = md5(random_bytes(20));
 
 //* Apache
 $conf['apache']['installed'] = false; // will be detected automatically during installation
diff --git a/install/dist/conf/debiantesting.conf.php b/install/dist/conf/debiantesting.conf.php
index cbc380fffb..3a06dfb86b 100644
--- a/install/dist/conf/debiantesting.conf.php
+++ b/install/dist/conf/debiantesting.conf.php
@@ -65,14 +65,14 @@ $conf['mysql']['admin_user'] = 'root';
 $conf['mysql']['admin_password'] = '';
 $conf['mysql']['charset'] = 'utf8';
 $conf['mysql']['ispconfig_user'] = 'ispconfig';
-$conf['mysql']['ispconfig_password'] = md5(uniqid(rand()));
+$conf['mysql']['ispconfig_password'] = md5(random_bytes(20));
 $conf['mysql']['master_slave_setup'] = 'n';
 $conf['mysql']['master_host'] = '';
 $conf['mysql']['master_database'] = 'dbispconfig';
 $conf['mysql']['master_admin_user'] = 'root';
 $conf['mysql']['master_admin_password'] = '';
 $conf['mysql']['master_ispconfig_user'] = '';
-$conf['mysql']['master_ispconfig_password'] = md5(uniqid(rand()));
+$conf['mysql']['master_ispconfig_password'] = md5(random_bytes(20));
 
 //* Apache
 $conf['apache']['installed'] = false; // will be detected automatically during installation
diff --git a/install/dist/conf/gentoo.conf.php b/install/dist/conf/gentoo.conf.php
index 24c7d0633e..23558a164d 100644
--- a/install/dist/conf/gentoo.conf.php
+++ b/install/dist/conf/gentoo.conf.php
@@ -63,14 +63,14 @@ $conf['mysql']['admin_user'] = 'root';
 $conf['mysql']['admin_password'] = '';
 $conf['mysql']['charset'] = 'utf8';
 $conf['mysql']['ispconfig_user'] = 'ispconfig';
-$conf['mysql']['ispconfig_password'] = md5(uniqid(rand()));
+$conf['mysql']['ispconfig_password'] = md5(random_bytes(20));
 $conf['mysql']['master_slave_setup'] = 'n';
 $conf['mysql']['master_host'] = '';
 $conf['mysql']['master_database'] = 'dbispconfig';
 $conf['mysql']['master_admin_user'] = 'root';
 $conf['mysql']['master_admin_password'] = '';
 $conf['mysql']['master_ispconfig_user'] = '';
-$conf['mysql']['master_ispconfig_password'] = md5(uniqid(rand()));
+$conf['mysql']['master_ispconfig_password'] = md5(random_bytes(20));
 
 //* SuPHP
 $conf['suphp']['config_file'] = '/etc/suphp.conf';
diff --git a/install/dist/conf/ubuntu1604.conf.php b/install/dist/conf/ubuntu1604.conf.php
index 0d3fe23bad..bd8d0bcd1c 100644
--- a/install/dist/conf/ubuntu1604.conf.php
+++ b/install/dist/conf/ubuntu1604.conf.php
@@ -65,14 +65,14 @@ $conf['mysql']['admin_user'] = 'root';
 $conf['mysql']['admin_password'] = '';
 $conf['mysql']['charset'] = 'utf8';
 $conf['mysql']['ispconfig_user'] = 'ispconfig';
-$conf['mysql']['ispconfig_password'] = md5(uniqid(rand()));
+$conf['mysql']['ispconfig_password'] = md5(random_bytes(20));
 $conf['mysql']['master_slave_setup'] = 'n';
 $conf['mysql']['master_host'] = '';
 $conf['mysql']['master_database'] = 'dbispconfig';
 $conf['mysql']['master_admin_user'] = 'root';
 $conf['mysql']['master_admin_password'] = '';
 $conf['mysql']['master_ispconfig_user'] = '';
-$conf['mysql']['master_ispconfig_password'] = md5(uniqid(rand()));
+$conf['mysql']['master_ispconfig_password'] = md5(random_bytes(20));
 
 //* Apache
 $conf['apache']['installed'] = false; // will be detected automatically during installation
diff --git a/install/dist/conf/ubuntu1710.conf.php b/install/dist/conf/ubuntu1710.conf.php
index 0730f8f2d5..d365388549 100644
--- a/install/dist/conf/ubuntu1710.conf.php
+++ b/install/dist/conf/ubuntu1710.conf.php
@@ -65,14 +65,14 @@ $conf['mysql']['admin_user'] = 'root';
 $conf['mysql']['admin_password'] = '';
 $conf['mysql']['charset'] = 'utf8';
 $conf['mysql']['ispconfig_user'] = 'ispconfig';
-$conf['mysql']['ispconfig_password'] = md5(uniqid(rand()));
+$conf['mysql']['ispconfig_password'] = md5(random_bytes(20));
 $conf['mysql']['master_slave_setup'] = 'n';
 $conf['mysql']['master_host'] = '';
 $conf['mysql']['master_database'] = 'dbispconfig';
 $conf['mysql']['master_admin_user'] = 'root';
 $conf['mysql']['master_admin_password'] = '';
 $conf['mysql']['master_ispconfig_user'] = '';
-$conf['mysql']['master_ispconfig_password'] = md5(uniqid(rand()));
+$conf['mysql']['master_ispconfig_password'] = md5(random_bytes(20));
 
 //* Apache
 $conf['apache']['installed'] = false; // will be detected automatically during installation
diff --git a/install/dist/conf/ubuntu1804.conf.php b/install/dist/conf/ubuntu1804.conf.php
index 2a09f787db..fa96f7a5ca 100644
--- a/install/dist/conf/ubuntu1804.conf.php
+++ b/install/dist/conf/ubuntu1804.conf.php
@@ -65,14 +65,14 @@ $conf['mysql']['admin_user'] = 'root';
 $conf['mysql']['admin_password'] = '';
 $conf['mysql']['charset'] = 'utf8';
 $conf['mysql']['ispconfig_user'] = 'ispconfig';
-$conf['mysql']['ispconfig_password'] = md5(uniqid(rand()));
+$conf['mysql']['ispconfig_password'] = md5(random_bytes(20));
 $conf['mysql']['master_slave_setup'] = 'n';
 $conf['mysql']['master_host'] = '';
 $conf['mysql']['master_database'] = 'dbispconfig';
 $conf['mysql']['master_admin_user'] = 'root';
 $conf['mysql']['master_admin_password'] = '';
 $conf['mysql']['master_ispconfig_user'] = '';
-$conf['mysql']['master_ispconfig_password'] = md5(uniqid(rand()));
+$conf['mysql']['master_ispconfig_password'] = md5(random_bytes(20));
 
 //* Apache
 $conf['apache']['installed'] = false; // will be detected automatically during installation
diff --git a/install/dist/conf/ubuntu2004.conf.php b/install/dist/conf/ubuntu2004.conf.php
index fe5a9b083b..28d4bf3c14 100644
--- a/install/dist/conf/ubuntu2004.conf.php
+++ b/install/dist/conf/ubuntu2004.conf.php
@@ -65,14 +65,14 @@ $conf['mysql']['admin_user'] = 'root';
 $conf['mysql']['admin_password'] = '';
 $conf['mysql']['charset'] = 'utf8';
 $conf['mysql']['ispconfig_user'] = 'ispconfig';
-$conf['mysql']['ispconfig_password'] = md5(uniqid(rand()));
+$conf['mysql']['ispconfig_password'] = md5(random_bytes(20));
 $conf['mysql']['master_slave_setup'] = 'n';
 $conf['mysql']['master_host'] = '';
 $conf['mysql']['master_database'] = 'dbispconfig';
 $conf['mysql']['master_admin_user'] = 'root';
 $conf['mysql']['master_admin_password'] = '';
 $conf['mysql']['master_ispconfig_user'] = '';
-$conf['mysql']['master_ispconfig_password'] = md5(uniqid(rand()));
+$conf['mysql']['master_ispconfig_password'] = md5(random_bytes(20));
 
 //* Apache
 $conf['apache']['installed'] = false; // will be detected automatically during installation
diff --git a/install/lib/installer_base.lib.php b/install/lib/installer_base.lib.php
index 103abaef19..3a3d415bb5 100644
--- a/install/lib/installer_base.lib.php
+++ b/install/lib/installer_base.lib.php
@@ -190,6 +190,7 @@ class installer_base {
 			$salt_length = 12;
 		}
 
+		// todo: replace the below with password_hash() when we drop php5.4 support
 		if(function_exists('openssl_random_pseudo_bytes')) {
 			$salt .= substr(bin2hex(openssl_random_pseudo_bytes($salt_length)), 0, $salt_length);
 		} else {
diff --git a/interface/lib/app.inc.php b/interface/lib/app.inc.php
index ee4713cd98..7ff158fbdc 100755
--- a/interface/lib/app.inc.php
+++ b/interface/lib/app.inc.php
@@ -28,6 +28,8 @@ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
+require_once 'compatibility.inc.php';
+
 //* Enable gzip compression for the interface
 ob_start('ob_gzhandler');
 
diff --git a/interface/lib/classes/auth.inc.php b/interface/lib/classes/auth.inc.php
index a69d43da2e..3a4cc1603c 100644
--- a/interface/lib/classes/auth.inc.php
+++ b/interface/lib/classes/auth.inc.php
@@ -231,7 +231,7 @@ class auth {
 	public function get_random_password($minLength = 8, $special = false) {
 		if($minLength < 8) $minLength = 8;
 		$maxLength = $minLength + 5;
-		$length = mt_rand($minLength, $maxLength);
+		$length = random_int($minLength, $maxLength);
 		
 		$alphachars = "abcdefghijklmnopqrstuvwxyz";
 		$upperchars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
@@ -240,28 +240,28 @@ class auth {
 		
 		$num_special = 0;
 		if($special == true) {
-			$num_special = intval(mt_rand(0, round($length / 4))) + 1;
+			$num_special = intval(random_int(0, round($length / 4))) + 1;
 		}
-		$numericlen = mt_rand(1, 2);
+		$numericlen = random_int(1, 2);
 		$alphalen = $length - $num_special - $numericlen;
 		$upperlen = intval($alphalen / 2);
 		$alphalen = $alphalen - $upperlen;
 		$password = '';
 		
 		for($i = 0; $i < $alphalen; $i++) {
-			$password .= substr($alphachars, mt_rand(0, strlen($alphachars) - 1), 1);
+			$password .= substr($alphachars, random_int(0, strlen($alphachars) - 1), 1);
 		}
 		
 		for($i = 0; $i < $upperlen; $i++) {
-			$password .= substr($upperchars, mt_rand(0, strlen($upperchars) - 1), 1);
+			$password .= substr($upperchars, random_int(0, strlen($upperchars) - 1), 1);
 		}
 		
 		for($i = 0; $i < $num_special; $i++) {
-			$password .= substr($specialchars, mt_rand(0, strlen($specialchars) - 1), 1);
+			$password .= substr($specialchars, random_int(0, strlen($specialchars) - 1), 1);
 		}
 		
 		for($i = 0; $i < $numericlen; $i++) {
-			$password .= substr($numchars, mt_rand(0, strlen($numchars) - 1), 1);
+			$password .= substr($numchars, random_int(0, strlen($numchars) - 1), 1);
 		}
 		
 		return str_shuffle($password);
@@ -298,8 +298,8 @@ class auth {
 	public function csrf_token_get($form_name) {
 		/* CSRF PROTECTION */
 		// generate csrf protection id and key
-		$_csrf_id = uniqid($form_name . '_'); // form id
-		$_csrf_key = sha1(uniqid(microtime(true), true)); // the key
+		$_csrf_id = $form_name . '_' . bin2hex(random_bytes(12)); // form id
+		$_csrf_key = sha1(random_bytes(20)); // the key
 		if(!isset($_SESSION['_csrf'])) $_SESSION['_csrf'] = array();
 		if(!isset($_SESSION['_csrf_timeout'])) $_SESSION['_csrf_timeout'] = array();
 		$_SESSION['_csrf'][$_csrf_id] = $_csrf_key;
diff --git a/interface/lib/classes/functions.inc.php b/interface/lib/classes/functions.inc.php
index 02d573a778..98cdc71a4f 100644
--- a/interface/lib/classes/functions.inc.php
+++ b/interface/lib/classes/functions.inc.php
@@ -28,6 +28,8 @@ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
+require_once 'compatibility.inc.php';
+
 //* The purpose of this library is to provide some general functions.
 //* This class is loaded automatically by the ispconfig framework.
 
@@ -437,10 +439,10 @@ class functions {
 		$iteration = 0;
 		$password = "";
 		$maxLength = $minLength + 5;
-		$length = $this->getRandomInt($minLength, $maxLength);
+		$length = random_int($minLength, $maxLength);
 
 		while($iteration < $length){
-			$randomNumber = (floor(((mt_rand() / mt_getrandmax()) * 100)) % 94) + 33;
+			$randomNumber = random_int(33, 126);
 			if(!$special){
 				if (($randomNumber >=33) && ($randomNumber <=47)) { continue; }
 				if (($randomNumber >=58) && ($randomNumber <=64)) { continue; }
@@ -455,10 +457,6 @@ class functions {
 		return $password;
 	}
 
-	public function getRandomInt($min, $max){
-		return floor((mt_rand() / mt_getrandmax()) * ($max - $min + 1)) + $min;
-	}
-
 	public function generate_customer_no(){
 		global $app;
 		// generate customer no.
@@ -474,14 +472,16 @@ class functions {
 		global $app;
 
 		// generate the SSH key pair for the client
-		$id_rsa_file = '/tmp/'.uniqid('',true);
+		$app->system->exec_safe('mktemp -dt id_rsa.XXXXXXXX');
+		$tmpdir = $app->system->last_exec_out();
+		$id_rsa_file = $tmpdir . uniqid('',true);
 		$id_rsa_pub_file = $id_rsa_file.'.pub';
 		if(file_exists($id_rsa_file)) unset($id_rsa_file);
 		if(file_exists($id_rsa_pub_file)) unset($id_rsa_pub_file);
 		if(!file_exists($id_rsa_file) && !file_exists($id_rsa_pub_file)) {
 			$app->system->exec_safe('ssh-keygen -t rsa -C ? -f ? -N ""', $username.'-rsa-key-'.time(), $id_rsa_file);
-			$app->db->query("UPDATE client SET created_at = UNIX_TIMESTAMP(), id_rsa = ?, ssh_rsa = ? WHERE client_id = ?", @file_get_contents($id_rsa_file), @file_get_contents($id_rsa_pub_file), $client_id);
-			$app->system->exec_safe('rm -f ? ?', $id_rsa_file, $id_rsa_pub_file);
+			$app->db->query("UPDATE client SET created_at = UNIX_TIMESTAMP(), id_rsa = ?, ssh_rsa = ? WHERE client_id = ?", $app->system->file_get_contents($id_rsa_file), $app->system->file_get_contents($id_rsa_pub_file), $client_id);
+			$app->system->rmdir($tmpdir, true);
 		} else {
 			$app->log("Failed to create SSH keypair for ".$username, LOGLEVEL_WARN);
 		}
diff --git a/interface/lib/classes/remoting.inc.php b/interface/lib/classes/remoting.inc.php
index 80e30bf849..23c123ec6c 100644
--- a/interface/lib/classes/remoting.inc.php
+++ b/interface/lib/classes/remoting.inc.php
@@ -139,7 +139,7 @@ class remoting {
 
 			//* Create a remote user session
 			//srand ((double)microtime()*1000000);
-			$remote_session = substr(str_shuffle('abcdefghijklmnopqrstuvwxyz'),0,1).sha1(mt_rand().uniqid('ispco',true));
+			$remote_session = bin2hex(random_bytes(20));
 			$remote_userid = $user['userid'];
 			$remote_functions = '';
 			$tstamp = time() + $this->session_timeout;
@@ -211,7 +211,7 @@ class remoting {
 				}
 				//* Create a remote user session
 				//srand ((double)microtime()*1000000);
-				$remote_session = substr(str_shuffle('abcdefghijklmnopqrstuvwxyz'),0,1).sha1(mt_rand().uniqid('ispco',true));
+				$remote_session = bin2hex(random_bytes(20));
 				$remote_userid = $remote_user['remote_userid'];
 				$remote_functions = $remote_user['remote_functions'];
 				$tstamp = time() + $this->session_timeout;
diff --git a/interface/lib/compatibility.inc.php b/interface/lib/compatibility.inc.php
new file mode 100644
index 0000000000..562e07ada4
--- /dev/null
+++ b/interface/lib/compatibility.inc.php
@@ -0,0 +1,80 @@
+<?php
+
+/*
+Copyright (c) 2021, Jesse Norell <jesse@kci.net>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+    * Neither the name of ISPConfig nor the names of its contributors
+      may be used to endorse or promote products derived from this software without
+      specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* random_bytes can be dropped when php 5.6 support is dropped */
+if (! function_exists('random_bytes')) {
+	function random_bytes($length) {
+		return openssl_random_pseudo_bytes($length);
+	}
+}
+
+/* random_int can be dropped when php 5.6 support is dropped */
+if (! function_exists('random_int')) {
+	function random_int($min=null, $max=null) {
+		if (null === $min) {
+			$min = PHP_INT_MIN;
+		}
+
+		if (null === $max) {
+			$min = PHP_INT_MAX;
+		}
+
+		if (!is_int($min) || !is_int($max)) {
+			trigger_error('random_int: $min and $max must be integer values', E_USER_NOTICE);
+			$min = (int)$min;
+			$max = (int)$max;
+		}
+
+		if ($min > $max) {
+			trigger_error('random_int: $max can\'t be lesser than $min', E_USER_WARNING);
+			return null;
+		}
+
+		$range = $counter = $max - $min;
+		$bits = 1;
+
+		while ($counter >>= 1) {
+			++$bits;
+		}
+
+		$bytes = (int)max(ceil($bits/8), 1);
+		$bitmask = pow(2, $bits) - 1;
+
+		if ($bitmask >= PHP_INT_MAX) {
+			$bitmask = PHP_INT_MAX;
+		}
+
+		do {
+			$result = hexdec(bin2hex(random_bytes($bytes))) & $bitmask;
+		} while ($result > $range);
+
+		return $result + $min;
+	}
+}
diff --git a/interface/web/login/password_reset.php b/interface/web/login/password_reset.php
index db4ad71c22..659075483c 100644
--- a/interface/web/login/password_reset.php
+++ b/interface/web/login/password_reset.php
@@ -71,7 +71,7 @@ if(isset($_POST['username']) && is_string($_POST['username']) && $_POST['usernam
 	} elseif ($continue) {
 		if($client['client_id'] > 0) {
 			$username = $client['username'];
-			$password_hash = sha1(uniqid('ispc_pw'));
+			$password_hash = sha1(random_bytes(20));
 			$app->db->query("UPDATE sys_user SET lost_password_reqtime = NOW(), lost_password_hash = ? WHERE username = ?", $password_hash, $username);
 
 			$server_domain = (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : $_SERVER['HTTP_HOST']);
diff --git a/server/lib/classes/functions.inc.php b/server/lib/classes/functions.inc.php
index 5296c3012b..34e5943cda 100644
--- a/server/lib/classes/functions.inc.php
+++ b/server/lib/classes/functions.inc.php
@@ -468,14 +468,16 @@ class functions {
 		global $app;
 
 		// generate the SSH key pair for the client
-		$id_rsa_file = '/tmp/'.uniqid('',true);
+		$app->system->exec_safe('mktemp -dt id_rsa.XXXXXXXX');
+		$tmpdir = $app->system->last_exec_out();
+		$id_rsa_file = $tmpdir . uniqid('',true);
 		$id_rsa_pub_file = $id_rsa_file.'.pub';
 		if(file_exists($id_rsa_file)) unset($id_rsa_file);
 		if(file_exists($id_rsa_pub_file)) unset($id_rsa_pub_file);
 		if(!file_exists($id_rsa_file) && !file_exists($id_rsa_pub_file)) {
 			$app->system->exec_safe('ssh-keygen -t rsa -C ? -f ? -N ""', $username.'-rsa-key-'.time(), $id_rsa_file);
 			$app->db->query("UPDATE client SET created_at = UNIX_TIMESTAMP(), id_rsa = ?, ssh_rsa = ? WHERE client_id = ?", $app->system->file_get_contents($id_rsa_file), $app->system->file_get_contents($id_rsa_pub_file), $client_id);
-			$app->system->exec_safe('rm -f ? ?', $id_rsa_file, $id_rsa_pub_file);
+			$app->system->rmdir($tmpdir, true);
 		} else {
 			$app->log("Failed to create SSH keypair for ".$username, LOGLEVEL_WARN);
 		}
diff --git a/server/lib/classes/letsencrypt.inc.php b/server/lib/classes/letsencrypt.inc.php
index ac805a6b67..c9f22f14c5 100644
--- a/server/lib/classes/letsencrypt.inc.php
+++ b/server/lib/classes/letsencrypt.inc.php
@@ -373,7 +373,7 @@ class letsencrypt {
 		$temp_domains = array_unique($temp_domains);
 
 		// check if domains are reachable to avoid letsencrypt verification errors
-		$le_rnd_file = uniqid('le-') . '.txt';
+		$le_rnd_file = uniqid('le-', true) . '.txt';
 		$le_rnd_hash = md5(uniqid('le-', true));
 		if(!is_dir('/usr/local/ispconfig/interface/acme/.well-known/acme-challenge/')) {
 			$app->system->mkdir('/usr/local/ispconfig/interface/acme/.well-known/acme-challenge/', false, 0755, true);
diff --git a/server/plugins-available/apache2_plugin.inc.php b/server/plugins-available/apache2_plugin.inc.php
index f2a121825b..35133ae322 100644
--- a/server/plugins-available/apache2_plugin.inc.php
+++ b/server/plugins-available/apache2_plugin.inc.php
@@ -296,16 +296,9 @@ class apache2_plugin {
 			if(file_exists($crt_file)) $app->system->rename($crt_file, $crt_file.'.bak');
 
 			$rand_file = $ssl_dir.'/random_file';
-			$rand_data = md5(uniqid(microtime(), 1));
-			for($i=0; $i<1000; $i++) {
-				$rand_data .= md5(uniqid(microtime(), 1));
-				$rand_data .= md5(uniqid(microtime(), 1));
-				$rand_data .= md5(uniqid(microtime(), 1));
-				$rand_data .= md5(uniqid(microtime(), 1));
-			}
-			$app->system->file_put_contents($rand_file, $rand_data);
+			$app->system->exec_safe('dd if=/dev/urandom of=? bs=256 count=1', $rand_file);
 
-			$ssl_password = substr(md5(uniqid(microtime(), 1)), 0, 15);
+			$ssl_password = bin2hex(random_bytes(12));
 
 			$ssl_cnf = "        RANDFILE               = $rand_file
 
diff --git a/server/plugins-available/nginx_plugin.inc.php b/server/plugins-available/nginx_plugin.inc.php
index 0e2cacaef9..9e111e72d9 100644
--- a/server/plugins-available/nginx_plugin.inc.php
+++ b/server/plugins-available/nginx_plugin.inc.php
@@ -129,16 +129,10 @@ class nginx_plugin {
 			if(file_exists($crt_file)) $app->system->rename($crt_file, $crt_file.'.bak');
 
 			$rand_file = $ssl_dir.'/random_file';
-			$rand_data = md5(uniqid(microtime(), 1));
-			for($i=0; $i<1000; $i++) {
-				$rand_data .= md5(uniqid(microtime(), 1));
-				$rand_data .= md5(uniqid(microtime(), 1));
-				$rand_data .= md5(uniqid(microtime(), 1));
-				$rand_data .= md5(uniqid(microtime(), 1));
-			}
-			$app->system->file_put_contents($rand_file, $rand_data);
+			$app->system->exec_safe('dd if=/dev/urandom of=? bs=256 count=1', $rand_file);
+
+			$ssl_password = bin2hex(random_bytes(12));
 
-			$ssl_password = substr(md5(uniqid(microtime(), 1)), 0, 15);
 
 			$ssl_cnf = "        RANDFILE               = $rand_file
 
-- 
GitLab