Просмотр исходного кода

Merge pull request #1016 from hestiacp/fix/1002-Web_Security_improvements

#1002 web security improvements
Jaap Marcus 5 лет назад
Родитель
Сommit
1034fed03b

+ 38 - 0
bin/v-delete-user-auth-log

@@ -0,0 +1,38 @@
+#!/bin/bash
+# info: Delete auth log file for user
+#
+# The function for deleting a users auth log file
+
+# Argument definition
+user=$1
+date=$(date "+%F %T")
+
+# Includes
+source $HESTIA/func/main.sh
+source $HESTIA/conf/hestia.conf
+
+# Perform verification if read-only mode is enabled
+check_hestia_demo_mode
+
+#----------------------------------------------------------#
+#                    Verifications                         #
+#----------------------------------------------------------#
+
+check_args '1' "$#" 'USER'
+is_format_valid 'user'
+is_object_valid 'user' 'USER' "$user"
+
+if [ ! -f $USER_DATA/auth.log ]; then
+    touch  $USER_DATA/auth.log
+fi
+
+#----------------------------------------------------------#
+#                       Action                             #
+#----------------------------------------------------------#
+
+rm $USER_DATA/auth.log
+
+log_history "Authentication log for $user was cleared on $date."
+log_event "$OK" "$ARGUMENTS"
+
+exit

+ 2 - 1
bin/v-list-sys-config

@@ -1,7 +1,7 @@
 #!/bin/bash
 # info: list system configuration
 # options: [FORMAT]
-# labels: 
+# labels:
 #
 # example: v-list-sys-config json
 #
@@ -61,6 +61,7 @@ json_list() {
         "DB_PMA_ALIAS": "'$DB_PMA_ALIAS'",
         "DB_PGA_ALIAS": "'$DB_PGA_ALIAS'",
         "LOGIN_STYLE": "'$LOGIN_STYLE'",
+        "INACTIVE_SESSION_TIMEOUT": "'$INACTIVE_SESSION_TIMEOUT'",
         "SOFTACULOUS": "'$SOFTACULOUS'"
     }
 }'

+ 118 - 0
bin/v-list-user-auth-log

@@ -0,0 +1,118 @@
+#!/bin/bash
+# info: list user log
+# options: USER [FORMAT]
+#
+# The function of obtaining the list of 10 last users commands.
+
+
+#----------------------------------------------------------#
+#                    Variable&Function                     #
+#----------------------------------------------------------#
+
+# Argument definition
+user=$1
+format=${2-shell}
+
+# Includes
+source $HESTIA/func/main.sh
+
+# JSON list function
+json_list() {
+    IFS=$'\n'
+    i=1
+    objects=$(echo "$logs" |wc -l)
+    echo "{"
+    for str in $logs; do
+        IP=$(echo "$str" |cut -f 2 -d \')
+        FINGERPRINT=$(echo "$str" |cut -f 4 -d \')
+        DATE=$(echo "$str" |cut -f 6 -d \')
+        TIME=$(echo "$str" |cut -f 8 -d \')
+        ACTIVE=$(echo "$str" |cut -f 10 -d \')
+        echo -n '    "'$i'": {
+            "IP": "'$IP'",
+            "FINGERPRINT": "'$FINGERPRINT'",
+            "TIME": "'$TIME'",
+            "DATE": "'$DATE'",
+            "ACTIVE": "'$ACTIVE'"
+        }'
+        if [ "$i" -lt "$objects" ]; then
+            echo ','
+        else
+            echo
+        fi
+        ((i++))
+        done
+    echo '}'
+}
+
+shell_list() {
+    IFS=$'\n'
+    echo "DATE~TIME~IP~FINGERPRINT~ACTIVE"
+    echo "----~----~--~-----------~------"
+    for str in $logs; do
+        IP=$(echo "$str" |cut -f 2 -d \')
+        FINGERPRINT=$(echo "$str" |cut -f 4 -d \')
+        DATE=$(echo "$str" |cut -f 6 -d \')
+        TIME=$(echo "$str" |cut -f 8 -d \')
+        ACTIVE=$(echo "$str" |cut -f 10 -d \')
+        echo "$DATE~$TIME~$IP~$FINGERPRINT~$ACTIVE"
+    done
+}
+
+# PLAIN list function
+plain_list() {
+    IFS=$'\n'
+    for str in $logs; do
+        IP=$(echo "$str" |cut -f 2 -d \')
+        FINGERPRINT=$(echo "$str" |cut -f 4 -d \')
+        DATE=$(echo "$str" |cut -f 6 -d \')
+        TIME=$(echo "$str" |cut -f 8 -d \')
+        ACTIVE=$(echo "$str" |cut -f 10 -d \')
+        echo -e "$DATE\t$TIME\t$IP\t$FINGERPRINT\t$ACTIVE"
+    done
+}
+
+# CSV list function
+csv_list() {
+    IFS=$'\n'
+    echo "ID,CMD,UNDO,TIME,DATE"
+    for str in $logs; do
+        IP=$(echo "$str" |cut -f 2 -d \')
+        FINGERPRINT=$(echo "$str" |cut -f 4 -d \')
+        DATE=$(echo "$str" |cut -f 6 -d \')
+        TIME=$(echo "$str" |cut -f 8 -d \')
+        ACTIVE=$(echo "$str" |cut -f 10 -d \')
+        echo "$DATE,$TIME,$IP,$FINGERPRINT,$ACTIVE"
+
+    done
+}
+
+#----------------------------------------------------------#
+#                    Verifications                         #
+#----------------------------------------------------------#
+
+check_args '1' "$#" 'USER [FORMAT]'
+is_format_valid 'user'
+is_object_valid 'user' 'USER' "$user"
+
+
+#----------------------------------------------------------#
+#                       Action                             #
+#----------------------------------------------------------#
+
+# Parsing history log
+logs=$(tail -n 10 $USER_DATA/auth.log 2>/dev/null)
+
+case $format in
+    json)   json_list ;;
+    plain)  plain_list ;;
+    csv)    csv_list ;;
+    shell)  shell_list |column -t -s '~';;
+esac
+
+
+#----------------------------------------------------------#
+#                       Hestia                             #
+#----------------------------------------------------------#
+
+exit

+ 45 - 0
bin/v-log-user-login

@@ -0,0 +1,45 @@
+#!/bin/bash
+# info: add user login
+# options: USER IP [FINGERPRINT]
+
+# Argument definition
+user=$1
+ip=$2
+fingerprint=${3}
+
+# Includes
+source $HESTIA/func/main.sh
+source $HESTIA/conf/hestia.conf
+
+#----------------------------------------------------------#
+#                    Verifications                         #
+#----------------------------------------------------------#
+
+check_args '2' "$#" 'USER IP [FINGERPRINT]'
+is_format_valid 'user' 'ip'
+is_object_valid 'user' 'USER' "$user"
+
+browser=$(echo $browser | sed -e "s/\'//g");
+
+# Generating timestamp
+time_n_date=$(date +'%T %F')
+time=$(echo "$time_n_date" |cut -f 1 -d \ )
+date=$(echo "$time_n_date" |cut -f 2 -d \ )
+
+if [ ! -f $USER_DATA/auth.log ]; then
+    touch  $USER_DATA/auth.log
+fi
+
+#----------------------------------------------------------#
+#                       Action                             #
+#----------------------------------------------------------#
+
+awk -i inplace -v finger="FINGERPRINT='$fingerprint'" -v active="active='no'" '$2 == finger {$5=active}1' $USER_DATA/auth.log   
+
+echo "IP='$ip' FINGERPRINT='$fingerprint' DATE='$date' TIME='$time' active='yes'" >> $USER_DATA/auth.log
+
+#----------------------------------------------------------#
+#                       Hestia                             #
+#----------------------------------------------------------#
+
+exit

+ 35 - 0
bin/v-log-user-logout

@@ -0,0 +1,35 @@
+#!/bin/bash
+# info: Log User logout event
+# options: USER FINGERPRINT
+
+# Argument definition
+user=$1
+fingerprint=$2
+
+# Includes
+source $HESTIA/func/main.sh
+source $HESTIA/conf/hestia.conf
+
+#----------------------------------------------------------#
+#                    Verifications                         #
+#----------------------------------------------------------#
+
+check_args '2' "$#" 'USER FINGERPRINT'
+is_format_valid 'user'
+is_object_valid 'user' 'USER' "$user"
+
+if [ ! -f $USER_DATA/auth.log ]; then
+    touch  $USER_DATA/auth.log
+fi
+
+#----------------------------------------------------------#
+#                       Action                             #
+#----------------------------------------------------------#
+
+awk -i inplace -v finger="FINGERPRINT='$fingerprint'" -v active="active='no'" '$2 == finger {$5=active}1' $USER_DATA/auth.log 
+
+#----------------------------------------------------------#
+#                       Hestia                             #
+#----------------------------------------------------------#
+
+exit

+ 7 - 0
func/upgrade.sh

@@ -126,6 +126,13 @@ upgrade_health_check() {
         echo "[ ! ] Adding missing variable to hestia.conf: LOGIN_STYLE ('default')"
         $BIN/v-change-sys-config-value "LOGIN_STYLE" "default"
     fi
+
+    # Inactive session timeout
+    if [ -z "$INACTIVE_SESSION_TIMEOUT" ]; then
+        echo "[ ! ] Adding missing variable to hestia.conf: INACTIVE_SESSION_TIMEOUT ('60')"
+        $BIN/v-change-sys-config-value "INACTIVE_SESSION_TIMEOUT" "60"
+    fi
+    
     
     echo "[ * ] Health check complete. Starting upgrade from $VERSION to $new_version..."
     echo "============================================================================="

+ 3 - 1
install/hst-install-debian.sh

@@ -1089,6 +1089,9 @@ echo "LANGUAGE='$lang'" >> $HESTIA/conf/hestia.conf
 # Login in screen
 echo "LOGIN_STYLE='default'" >> $HESTIA/conf/hestia.conf
 
+# Inactive session timeout
+echo "INACTIVE_SESSION_TIMEOUT='60'" >> $HESTIA/conf/hestia.conf
+
 # Version & Release Branch
 echo "VERSION='${HESTIA_INSTALL_VER}'" >> $HESTIA/conf/hestia.conf
 echo "RELEASE_BRANCH='release'" >> $HESTIA/conf/hestia.conf
@@ -1687,7 +1690,6 @@ if [ "$mysql" = 'yes' ]; then
     source $HESTIA_INSTALL_DIR/phpmyadmin/pma.sh > /dev/null 2>&1
 fi
 
-
 #----------------------------------------------------------#
 #                   Configure Admin User                   #
 #----------------------------------------------------------#

+ 3 - 1
install/hst-install-ubuntu.sh

@@ -1140,6 +1140,9 @@ echo "LANGUAGE='$lang'" >> $HESTIA/conf/hestia.conf
 # Login in screen
 echo "LOGIN_STYLE='default'" >> $HESTIA/conf/hestia.conf
 
+# Inactive session timeout
+echo "INACTIVE_SESSION_TIMEOUT='60'" >> $HESTIA/conf/hestia.conf
+
 # Version & Release Branch
 echo "VERSION='${HESTIA_INSTALL_VER}'" >> $HESTIA/conf/hestia.conf
 echo "RELEASE_BRANCH='release'" >> $HESTIA/conf/hestia.conf
@@ -1722,7 +1725,6 @@ else
     echo "API='no'" >> $HESTIA/conf/hestia.conf
 fi
 
-
 #----------------------------------------------------------#
 #                      Fix phpmyadmin                      #
 #----------------------------------------------------------#

+ 1 - 1
install/upgrade/versions/1.2.2.sh

@@ -45,4 +45,4 @@ if [ -f "$apt/postgresql.list" ]; then
         echo "  ----- PostgreSQL"
         sed -i "s/http\:\/\/apt.postgresql.org/https\:\/\/apt.postgresql.org/g" $apt/postgresql.list
     fi
-fi
+fi

+ 40 - 0
web/delete/user/log/index.php

@@ -0,0 +1,40 @@
+<?php
+// Init
+error_reporting(NULL);
+session_start();
+include($_SERVER['DOCUMENT_ROOT']."/inc/main.php");
+
+// Check token
+if ((!isset($_GET['token'])) || ($_SESSION['token'] != $_GET['token'])) {
+    header('location: /login/');
+    exit();
+}
+
+// Clear log
+$v_username = escapeshellarg($user);
+exec (HESTIA_CMD."v-delete-user-auth-log ".$v_username, $output, $return_var);
+//check_return_code($return_var,$output);
+//unset($output);
+
+
+$ip = $_SERVER['REMOTE_ADDR'];
+if(isset($_SERVER['HTTP_CF_CONNECTING_IP'])){
+    if(!empty($_SERVER['HTTP_CF_CONNECTING_IP'])){
+        $ip = $_SERVER['HTTP_CF_CONNECTING_IP'];
+    }
+} 
+$v_ip = escapeshellarg($ip);
+    
+$v_murmur = escapeshellarg($_SESSION['MURMUR']);
+exec(HESTIA_CMD."v-log-user-login ".$v_username." ".$v_ip." ".$v_murmur, $output, $return_var);
+
+// Render page
+//render_page($user, $TAB, 'list_auth');
+
+// Flush session messages
+unset($_SESSION['error_msg']);
+unset($_SESSION['ok_msg']);
+
+header("Location: /edit/user/log/?user=".$_SESSION['user']);
+
+exit;

+ 14 - 3
web/edit/server/index.php

@@ -533,15 +533,26 @@ if (!empty($_POST['save'])) {
 
     // Change login style
     if (empty($_SESSION['error_msg'])) {
-        if ($_POST['v_login_style'] != $_SESSION['LOGIN_STYLE']) {
-            exec (HESTIA_CMD."v-change-sys-config-value LOGIN_STYLE ".escapeshellarg($_POST['v_login_style']), $output, $return_var);
+        if ($_POST['v_inactive_session_timeout'] != $_SESSION['INACTIVE_SESSION_TIMEOUT']) {
+            exec (HESTIA_CMD."v-change-sys-config-value INACTIVE_SESSION_TIMEOUT ".escapeshellarg($_POST['v_inactive_session_timeout']), $output, $return_var);
             check_return_code($return_var,$output);
             unset($output);
-            if (empty($_SESSION['error_msg'])) $v_login_style = $_POST['v_login_style'];
+            if (empty($_SESSION['error_msg'])) $v_login_style = $_POST['v_inactive_session_timeout'];
             $v_security_adv = 'yes';
         }
     }
 
+// Change login style
+if (empty($_SESSION['error_msg'])) {
+    if ($_POST['v_login_style'] != $_SESSION['LOGIN_STYLE']) {
+        exec (HESTIA_CMD."v-change-sys-config-value LOGIN_STYLE ".escapeshellarg($_POST['v_login_style']), $output, $return_var);
+        check_return_code($return_var,$output);
+        unset($output);
+        if (empty($_SESSION['error_msg'])) $v_login_style = $_POST['v_login_style'];
+        $v_security_adv = 'yes';
+    }
+}
+
     // Update SSL certificate
     if ((!empty($_POST['v_ssl_crt'])) && (empty($_SESSION['error_msg']))) {
         if (($v_ssl_crt != str_replace("\r\n", "\n",  $_POST['v_ssl_crt'])) || ($v_ssl_key != str_replace("\r\n", "\n",  $_POST['v_ssl_key']))) {

+ 35 - 0
web/edit/user/log/index.php

@@ -0,0 +1,35 @@
+<?php
+error_reporting(NULL);
+ob_start();
+$TAB = 'USER';
+
+// Main include
+include($_SERVER['DOCUMENT_ROOT']."/inc/main.php");
+
+
+// Check user argument
+if (empty($_GET['user'])) {
+    header("Location: /list/user/");
+    exit;
+}
+
+// Edit as someone else?
+if (($_SESSION['user'] == 'admin') && (!empty($_GET['user']))) {
+    $user=$_GET['user'];
+    $v_username=$_GET['user'];
+} else {
+    $user=$_SESSION['user'];
+    $v_username=$_SESSION['user'];
+}
+exec(HESTIA_CMD."v-list-user-auth-log ".escapeshellarg($v_username)." json", $output, $return_var);
+check_return_code($return_var,$output);
+$data = json_decode(implode('', $output), true);
+$data = array_reverse($data);
+unset($output);
+
+// Render page
+render_page($user, $TAB, 'list_auth');
+
+// Flush session messages
+unset($_SESSION['error_msg']);
+unset($_SESSION['ok_msg']);

+ 18 - 0
web/inc/main.php

@@ -38,6 +38,9 @@ if(!isset($_SESSION['user_combined_ip'])){
 
 // Checking user to use session from the same IP he has been logged in
 if($_SESSION['user_combined_ip'] != $user_combined_ip && $_SERVER['REMOTE_ADDR'] != '127.0.0.1'){
+    $v_user = escapeshellarg($_SESSION['user']);
+    $v_murmur = escapeshellarg($_SESSION['MURMUR']);
+    exec(HESTIA_CMD."v-log-user-logout ".$v_user." ".$v_murmur, $output, $return_var);
     session_destroy();
     session_start();
     $_SESSION['request_uri'] = $_SERVER['REQUEST_URI'];
@@ -71,6 +74,21 @@ if (isset($_SESSION['user'])) {
     }
 }
 
+if( NO_AUTH_REQUIRED !== true){
+    if(empty($_SESSION['LAST_ACTIVITY']) || empty($_SESSION['INACTIVE_SESSION_TIMEOUT'])){
+        session_destroy();
+        header("Location: /login/");
+    }else if ($_SESSION['INACTIVE_SESSION_TIMEOUT'] * 60 + $_SESSION['LAST_ACTIVITY'] < time()) {
+        $v_user = escapeshellarg($_SESSION['user']);
+        $v_murmur = escapeshellarg($_SESSION['MURMUR']);
+        exec(HESTIA_CMD."v-log-user-logout ".$v_user." ".$v_murmur, $output, $return_var);
+        session_destroy();
+        header("Location: /login/");
+    }else{
+        $_SESSION['LAST_ACTIVITY'] = time();
+    }
+}
+
 if (isset($_SESSION['user'])) {
     $user = $_SESSION['user'];
 }

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
web/js/fingerprint2.min.js


+ 35 - 34
web/login/index.php

@@ -1,7 +1,6 @@
 <?php
 
 define('NO_AUTH_REQUIRED',true);
-
 // Main include
 include($_SERVER['DOCUMENT_ROOT']."/inc/main.php");
 
@@ -9,11 +8,10 @@ $TAB = 'login';
 
 // Logout
 if (isset($_GET['logout'])) {
+    setcookie('limit2fa','',time() - 3600,"/");
     session_destroy();
 }
 
-
-
 // Login as someone else
 if (isset($_SESSION['user'])) {
     if (empty($_GET['loginas']) ){
@@ -41,14 +39,14 @@ function authenticate_user($user, $password, $twofa = ''){
     unset($_SESSION['login']);
     if(isset($_SESSION['token']) && isset($_POST['token']) && $_POST['token'] == $_SESSION['token']) {
     $v_user = escapeshellarg($user);
-    $v_ip = escapeshellarg($_SERVER['REMOTE_ADDR']);
+    $ip = $_SERVER['REMOTE_ADDR'];
     if(isset($_SERVER['HTTP_CF_CONNECTING_IP'])){
         if(!empty($_SERVER['HTTP_CF_CONNECTING_IP'])){
-            $v_ip = escapeshellarg($_SERVER['HTTP_CF_CONNECTING_IP']);
+            $ip = $_SERVER['HTTP_CF_CONNECTING_IP'];
         }
-    }    
-    
-    // Get user's salt
+    }
+    $v_ip = escapeshellarg($ip);
+     // Get user's salt
     $output = '';
     exec (HESTIA_CMD."v-get-user-salt ".$v_user." ".$v_ip." json" , $output, $return_var);
     $pam = json_decode(implode('', $output), true);
@@ -90,30 +88,28 @@ function authenticate_user($user, $password, $twofa = ''){
                 $error = "<a class=\"error\">"._('Invalid username or password')."</a>";
                 return $error;
             } else {
+
                 // Get user speciefic parameters
                 exec (HESTIA_CMD . "v-list-user ".$v_user." json", $output, $return_var);
                 $data = json_decode(implode('', $output), true);
-                unset($output);
-                // Check if 2FA is active
                 if ($data[$user]['TWOFA'] != '') {
-                   if (empty($twofa)){
-                            $_SESSION['login']['username'] = $user;
-                            $_SESSION['login']['password'] = $password;
+                        if(empty($_POST['twofa'])){
                             return false;
-                   } else {
-                        $v_twofa = escapeshellarg($twofa);
-                        exec(HESTIA_CMD ."v-check-user-2fa ".$v_user." ".$v_twofa, $output, $return_var);
-                        unset($output);
-                        if ( $return_var > 0 ) {
-                            //sleep(2);
-                            $error = "<a class=\"error\">"._('Invalid or missing 2FA token')."</a>";
-                            $_SESSION['login']['username'] = $user;
-                            $_SESSION['login']['password'] = $password;
-                            return $error;
+                        }else{
+                            $v_twofa = $_POST['twofa'];
+                            exec(HESTIA_CMD ."v-check-user-2fa ".$v_user." ".$v_twofa, $output, $return_var);
+                            unset($output);
+                            if ( $return_var > 0 ) {
+                                sleep(2);
+                                $error = "<a class=\"error\">"._('Invalid or missing 2FA token')."</a>";
+                                $_SESSION['login']['username'] = $user;
+                                $_SESSION['login']['password'] = $password;
+                                return $error;
+                                unset($_POST['twofa']);
+                            }
                         }
-                   }
                 }
-                
+
                 if ($data[$user]['ROLE'] == 'admin'){
                     exec (HESTIA_CMD . "v-list-user admin json", $output, $return_var);
                     $data = json_decode(implode('', $output), true);
@@ -122,6 +118,12 @@ function authenticate_user($user, $password, $twofa = ''){
                 // Define session user
                 $_SESSION['user'] = key($data);
                 $v_user = $_SESSION['user'];
+                //log successfull login attempt
+                $v_murmur = escapeshellarg($_POST['murmur']);
+                exec(HESTIA_CMD."v-log-user-login ".$v_user." ".$v_ip." ".$v_murmur, $output, $return_var);
+
+                $_SESSION['LAST_ACTIVITY'] = time();
+                $_SESSION['MURMUR'] = $_POST['murmur'];
 
                 // Define language
                 $output = '';
@@ -132,7 +134,6 @@ function authenticate_user($user, $password, $twofa = ''){
                 } else {
                     $_SESSION['language'] = 'en';
                 }
-
                 // Regenerate session id to prevent session fixation
                 session_regenerate_id();
 
@@ -161,11 +162,11 @@ function authenticate_user($user, $password, $twofa = ''){
     }
 }
 if (!empty($_SESSION['login']['username']) && !empty($_SESSION['login']['password']) && !empty($_POST['twofa'])){
-    $error = authenticate_user($_SESSION['login']['username'], $_SESSION['login']['password'], $_POST['twofa']); 
+    $error = authenticate_user($_SESSION['login']['username'], $_SESSION['login']['password'], $_POST['twofa']);
     unset($_POST);
 } else if (!empty($_POST['user']) && !empty($_POST['password'])) {
-    $error = authenticate_user($_POST['user'], $_POST['password']); 
-    unset($_POST);   
+    $error = authenticate_user($_POST['user'], $_POST['password']);
+    unset($_POST);
 }else{
     unset($_SESSION['login']);
 }
@@ -195,20 +196,20 @@ $_SESSION['token'] = md5(uniqid(mt_rand(), true));
 
 require_once('../templates/header.html');
 if(!empty($_SESSION['login'])){
-    require_once('../templates/login_2.html');    
+    require_once('../templates/login_2.html');
 }else if (empty($_POST['user'])) {
     if($_SESSION['LOGIN_STYLE'] == 'old'){
-        require_once('../templates/login_a.html'); 
+        require_once('../templates/login_a.html');
     }else{
-        require_once('../templates/login.html');        
+        require_once('../templates/login.html');
     }
 }else if (empty($_POST['password'])) {
     require_once('../templates/login_1.html');
 }else{
     if($_SESSION['LOGIN_STYLE'] == 'old'){
-        require_once('../templates/login_a.html'); 
+        require_once('../templates/login_a.html');
     }else{
-        require_once('../templates/login.html');        
+        require_once('../templates/login.html');
     }
 }
 ?>

+ 8 - 0
web/logout/index.php

@@ -1,11 +1,19 @@
 <?php
 session_start();
 
+define('HESTIA_CMD', '/usr/bin/sudo /usr/local/hestia/bin/');
+
 if (!empty($_SESSION['look'])) {
 
     unset($_SESSION['look']);
     header("Location: /");
 } else {
+    if($_SESSION['MURMUR'] && $_SESSION['user']){
+        $v_user = escapeshellarg($_SESSION['user']);
+        $v_murmur = escapeshellarg($_SESSION['MURMUR']);
+        exec(HESTIA_CMD."v-log-user-logout ".$v_user." ".$v_murmur, $output, $return_var);
+    }
+    
     session_destroy();
     header("Location: /login/");
 }

+ 11 - 0
web/templates/admin/edit_server.html

@@ -866,6 +866,17 @@
                                                 <br><br>
                                             </td>
                                         </tr>
+                                        <tr>
+                                            <td class="vst-text input-label">
+                                                <?php print _('Inactive session length');?> (<?php print _('Minutes');?>)
+                                            </td>
+                                        </tr>
+                                        <tr>
+                                            <td>
+                                                <input type="text" size="20" class="vst-input" name="v_inactive_session_timeout" value="<?=trim($_SESSION['INACTIVE_SESSION_TIMEOUT'], "'")?>">
+                                                <br><br>
+                                            </td>
+                                        </tr>
                                     </table>
                                 </td>
                             </tr>

+ 2 - 3
web/templates/admin/edit_user.html

@@ -2,9 +2,8 @@
         <div class="l-sort clearfix">
           <div class="l-unit-toolbar__buttonstrip">
             <a class="ui-button cancel" id="btn-back" href="/list/user/"><i class="fas fa-arrow-left status-icon blue"></i> <?=_('Back')?></a>
-            <?php if( $_SESSION['user'] == $_GET['user'] || isset($_SESSION['look'])){?>
-                <a href="/list/key/" id="btn-create" class="ui-button cancel"><i class="fas fa-key status-icon orange"></i><?=_('Manage SSH keys')?></a>
-            <?php } ?>
+            <a href="/list/key/" id="btn-create" class="ui-button cancel" title="<?=_('Manage SSH keys');?>"><i class="fas fa-key status-icon orange"></i><?=_('Manage SSH keys')?></a>
+            <a href="/edit/user/log/?user=<?php echo $_SESSION['user'];?>" id="btn-list" class="ui-button cancel" title="<?=_('Login history');?>"><i class="fas fa-key status-icon orange"></i><?=_('Login history')?></a>
           </div>
           <div class="l-unit-toolbar__buttonstrip float-right">
             <a href="#" class="ui-button" data-action="submit" data-id="vstobjects"><i class="fas fa-save status-icon purple"></i> <?=_('Save')?></a>

+ 71 - 0
web/templates/admin/list_auth.html

@@ -0,0 +1,71 @@
+<div class="l-center">
+      <div class="l-sort clearfix noselect">
+        <div class="l-unit-toolbar__buttonstrip">
+          <a href="javascript:window.history.back()" id="btn-back" class="ui-button cancel"><i class="fas fa-arrow-left status-icon blue"></i> <?=_('Back')?></a>
+          <a href="javascript:location.reload();" class="ui-button cancel"><i class="fas fa-redo status-icon green"></i> <?=_('Refresh')?></a>
+          <div class="actions-panel display-inline-block" key-action="js">
+            <a class="data-controls do_delete ui-button danger cancel">
+              <i class="do_delete fas fa-times-circle status-icon red"></i>
+              <?=_('Delete')?>
+              <input type="hidden" name="delete_url" value="/delete/user/log/?token=<?=$_SESSION['token']?>" />
+                <div class="confirmation-text-delete hidden" title="<?=_('Confirmation')?>">
+                  <p class="confirmation"><?=_('Delete authentication logs')?></p>
+                </div>
+            </a>
+          </div>
+        </div>
+        <div class="l-sort-toolbar clearfix">
+        </div>
+      </div>
+    </div>
+
+<div class="l-separator"></div>
+
+<div class="l-center units animated fadeIn">
+<div class="header table-header">     
+    <div class="l-unit__col l-unit__col--right">
+      <div class="clearfix l-unit__stat-col--left super-compact">
+        &nbsp;
+      </div>       
+      <div class="clearfix l-unit__stat-col--left small"><b><?php print _('Date');?></b></div>
+      <div class="clearfix l-unit__stat-col--left compact"><b><?php print _('Time');?></b></div>
+      <div class="clearfix l-unit__stat-col--left "><b><?php print _('IP address');?></b></div>
+      <div class="clearfix l-unit__stat-col--left "><b><?php print _('Active');?></b></div>
+      <div class="clearfix l-unit__stat-col--left wide-4"><b><?php print _('Browser Fingerprint');?></b></div>
+    </div>
+  </div> 
+<?php
+  foreach ($data as $key => $value) {
+    ++$i;
+  ?>
+  <div class="l-unit header">
+    <div class="l-unit__col l-unit__col--right">
+      <div class="clearfix l-unit__stat-col--left super-compact">
+        <i class="fas fa-info-circle status-icon dim"></i>
+      </div>
+      <div class="clearfix l-unit__stat-col--left small"><b><?=translate_date($data[$key]['DATE'])?></b></div>
+      <div class="clearfix l-unit__stat-col--left compact"><b><?=$data[$key]['TIME']?></b></div>
+      <div class="clearfix l-unit__stat-col--left "><?=$data[$key]['IP']?></div>
+      <div class="clearfix l-unit__stat-col--left "><?=$data[$key]['ACTIVE']?></div>
+      <div class="clearfix l-unit__stat-col--left "><?=$data[$key]['FINGERPRINT']?></div>
+      
+      
+    </div>
+  </div>
+<?}?>
+
+</div>
+
+<div id="vstobjects">
+  <div class="l-separator"></div>
+  <div class="l-center">
+    <div class="l-unit-ft">
+      <table class='data'></table>
+      <div class="data-count l-unit_col l-unit_col--right clearfix">
+        <?
+          printf(ngettext('%d log record', '%d log records', $i),$i);
+        ?>
+      </div>
+    </div>
+  </div>
+</div>

+ 22 - 5
web/templates/header.html

@@ -4,12 +4,11 @@
   <meta charset="utf-8">
   <link rel="icon" href="/images/favicon.ico" type="image/x-icon">
   <title><?php echo $_SERVER['HTTP_HOST']; ?> - <?=_($TAB)?> - <?=_('Hestia Control Panel');?></title>
-  <link type="text/css" rel="stylesheet" href="/css/styles.min.css?<?php echo JS_LATEST_UPDATE; ?>" />
+  <link type="text/css" rel="stylesheet" href="/css/styles.min.css?<?=JS_LATEST_UPDATE?>" />
   <link type="text/css" rel="stylesheet" href="/css/active-theme.css?<?php echo rand(); ?>" />
-  <link type="text/css" href="/css/animate.min.css?<?php echo JS_LATEST_UPDATE; ?>" rel="stylesheet" />
-  <link type="text/css" href="/css/jquery-custom-dialogs.css?<?php echo JS_LATEST_UPDATE; ?>" rel="stylesheet" />
-  <link type="text/css" href="/css/all.min.css?<?php echo JS_LATEST_UPDATE; ?>" rel="stylesheet" />
-  <script src="/inc/jquery/jquery-3.5.1.min.js"></script>
+  <link type="text/css" href="/css/animate.min.css?<?=JS_LATEST_UPDATE?>" rel="stylesheet" />
+  <link type="text/css" href="/css/jquery-custom-dialogs.css?<?=JS_LATEST_UPDATE?>" rel="stylesheet" />
+  <link type="text/css" href="/css/all.min.css?<?=JS_LATEST_UPDATE?>" rel="stylesheet" />
   <script>
     //
     //  GLOBAL SETTINGS
@@ -19,6 +18,24 @@
     GLOBAL.DB_USER_PREFIX   = 'admin_';
     GLOBAL.DB_DBNAME_PREFIX = 'admin_';
     GLOBAL.AJAX_URL = '';
+
+    if (window.requestIdleCallback) {
+        requestIdleCallback(function () {
+            Fingerprint2.get(function (components) {
+                var values = components.map(function (component) { return component.value })
+                var murmur = Fingerprint2.x64hash128(values.join(''), 31);
+                $('#murmur').val(murmur);
+            })
+        })
+    } else {
+        setTimeout(function () {
+                Fingerprint2.get(function (components) {
+                var values = components.map(function (component) { return component.value })
+                var murmur = Fingerprint2.x64hash128(values.join(''), 31);
+                $('#murmur').val(murmur);
+            })
+        }, 500);
+    }
   </script>
 </head>
 <body class="body-<?=strtolower($TAB)?> lang-<?=$_SESSION['language']?>">

+ 3 - 1
web/templates/login.html

@@ -10,6 +10,7 @@
                                 <td style="padding: 40px 60px 0 0;">
                                     <form method="post" action="/login/" id="form_login">
                                     <input type="hidden" name="token" value="<?php echo $_SESSION['token']; ?>">
+                                    <input type="hidden" name="murmur" value="" id="murmur">
                                     <table class="login-box">
                                         <tr>
                                             <td style="padding: 12px 0 0 2px;" class="login-welcome">
@@ -50,6 +51,7 @@
                 </tr>
             </table>
         </center>
-
+        <script src="/inc/jquery/jquery-3.5.1.min.js"></script>
+        <script type="text/javascript" src="/js/fingerprint2.min.js?<?=JS_LATEST_UPDATE?>"></script>
     </body>
 </html>

+ 3 - 1
web/templates/login_1.html

@@ -11,7 +11,7 @@
                                     <form method="post" action="/login/" id="form_login">
                                     <input type="hidden" name="token" value="<?php echo $_SESSION['token']; ?>">
                                     <input type="hidden" name="user" value="<?php echo $_POST['user']; ?>">
-
+                                    <input type="hidden" name="murmur" value="<?php echo $_POST['murmur']; ?>" id="murmur">    
                                     <table class="login-box">
                                         <tr>
                                             <td style="padding: 12px 0 0 2px;" class="login-welcome">
@@ -58,5 +58,7 @@
                 </tr>
             </table>
         </center>
+        <script src="/inc/jquery/jquery-3.5.1.min.js"></script>
+        <script type="text/javascript" src="/js/fingerprint2.min.js?<?=JS_LATEST_UPDATE?>"></script>
     </body>
 </html>

+ 3 - 0
web/templates/login_2.html

@@ -10,6 +10,7 @@
                                 <td style="padding: 40px 60px 0 0;" class="animated fadeIn">
                                     <form method="post" action="/login/" id="form_login">
                                     <input type="hidden" name="token" value="<?php echo $_SESSION['token']; ?>">
+                                    <input type="hidden" name="murmur" value="" id="murmur">
                                     <table class="login-box">
                                         <tr>
                                             <td style="padding: 12px 0 0 2px;" class="login-welcome">
@@ -55,5 +56,7 @@
                 </tr>
             </table>
         </center>
+        <script src="/inc/jquery/jquery-3.5.1.min.js"></script>
+        <script type="text/javascript" src="/js/fingerprint2.min.js?<?=JS_LATEST_UPDATE?>"></script>
     </body>
 </html>

+ 3 - 1
web/templates/login_a.html

@@ -10,6 +10,7 @@
                                 <td style="padding: 40px 60px 0 0;">
                                     <form method="post" action="/login/" id="form_login">
                                     <input type="hidden" name="token" value="<?php echo $_SESSION['token']; ?>">
+                                    <input type="hidden" name="murmur" value="" id="murmur">
                                     <table class="login-box">
                                         <tr>
                                             <td style="padding: 12px 0 0 2px;" class="login-welcome">
@@ -60,6 +61,7 @@
                 </tr>
             </table>
         </center>
-
+        <script src="/inc/jquery/jquery-3.5.1.min.js"></script>
+        <script type="text/javascript" src="/js/fingerprint2.min.js?<?=JS_LATEST_UPDATE?>"></script>
     </body>
 </html>

+ 2 - 1
web/templates/scripts.html

@@ -1,3 +1,4 @@
+  <script type="text/javascript" src="/inc/jquery/jquery-3.5.1.min.js"></script> 
   <script type="text/javascript" src="/js/jquery/jquery-1.7.2.min.js?<?=JS_LATEST_UPDATE?>"></script>
   <script type="text/javascript" src="/js/jquery/jquery.cookie.js?<?=JS_LATEST_UPDATE?>"></script>
   <script type="text/javascript" src="/js/jquery/jquery-ui-1.8.20.custom.min.js?<?=JS_LATEST_UPDATE?>"></script>
@@ -8,7 +9,7 @@
   <script type="text/javascript" src="/js/init.js?<?=JS_LATEST_UPDATE?>"></script>
   <script type="text/javascript" src="/js/i18n.js.php?<?=JS_LATEST_UPDATE?>"></script>
   <script type="text/javascript" src="/js/templates.js?<?=JS_LATEST_UPDATE?>"></script>
-
+  <script type="text/javascript" src="/js/fingerprint2.min.js?<?=JS_LATEST_UPDATE?>"></script>
   <script>
     $(function() {
       hover_menu();

Некоторые файлы не были показаны из-за большого количества измененных файлов