Skip to content

Commit 9e37a51

Browse files
authored
Merge pull request hestiacp#2115 from jaapmarcus/feature/reset-pw-f2b
Improve securtity against csfr/bruteforce
2 parents e4364b7 + 926a9c6 commit 9e37a51

File tree

8 files changed

+127
-95
lines changed

8 files changed

+127
-95
lines changed

bin/v-log-user-login

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ ip=$2
88
status=$3
99
session_id=$4
1010
user_agent=$5
11+
authlog="${6-no}"
12+
reason="${7}"
13+
1114

1215
active="yes"
1316
if [ "$status" = "failed" ]; then
@@ -24,7 +27,7 @@ source $HESTIA/conf/hestia.conf
2427
# Verifications #
2528
#----------------------------------------------------------#
2629

27-
check_args '2' "$#" 'USER IP SESSION_ID USER_AGENT'
30+
check_args '2' "$#" 'USER IP SESSION_ID USER_AGENT [AUTHLOG] [REASON]'
2831
is_format_valid 'user' 'ip'
2932
is_object_valid 'user' 'USER' "$user"
3033

@@ -43,8 +46,13 @@ fi
4346

4447
echo "DATE='$date' TIME='$time' IP='$ip' ACTION='login' STATUS='$status' USER_AGENT='$user_agent' SESSION='$session_id' ACTIVE='$active'" >> $USER_DATA/auth.log
4548

49+
if [ "$authlog" = "yes" ]; then
50+
echo "$date $time $user $ip failed to login ($reason)" >> $HESTIA/log/auth.log
51+
fi
52+
4653
#----------------------------------------------------------#
4754
# Hestia #
4855
#----------------------------------------------------------#
4956

57+
echo "Test" >> $HESTIA/log/system.log
5058
exit

web/login/index.php

Lines changed: 61 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
define('NO_AUTH_REQUIRED',true);
3+
define('NO_AUTH_REQUIRED', true);
44
// Main include
55
include($_SERVER['DOCUMENT_ROOT'] . '/inc/main.php');
66

@@ -27,14 +27,14 @@
2727
} else {
2828
$v_user = escapeshellarg($_GET['loginas']);
2929
$v_impersonator = escapeshellarg($_SESSION['user']);
30-
exec (HESTIA_CMD . "v-list-user ".$v_user." json", $output, $return_var);
31-
if ( $return_var == 0 ) {
30+
exec(HESTIA_CMD . "v-list-user ".$v_user." json", $output, $return_var);
31+
if ($return_var == 0) {
3232
$data = json_decode(implode('', $output), true);
3333
reset($data);
3434
$_SESSION['look'] = key($data);
3535
// Log impersonation events
36-
exec (HESTIA_CMD . 'v-log-action ' . $v_impersonator . " 'Info' 'Security' 'Logged in as another user (User: $v_user)'", $output, $return_var);
37-
exec (HESTIA_CMD . "v-log-action system 'Warning' 'Security' 'User impersonation session started (User: $v_user, Administrator: $v_impersonator)'", $output, $return_var);
36+
exec(HESTIA_CMD . 'v-log-action ' . $v_impersonator . " 'Info' 'Security' 'Logged in as another user (User: $v_user)'", $output, $return_var);
37+
exec(HESTIA_CMD . "v-log-action system 'Warning' 'Security' 'User impersonation session started (User: $v_user, Administrator: $v_impersonator)'", $output, $return_var);
3838
// Reset account details for File Manager to impersonated user
3939
unset($_SESSION['_sf2_attributes']);
4040
unset($_SESSION['_sf2_meta']);
@@ -51,14 +51,14 @@
5151
header('Location: /list/user/');
5252
exit;
5353
}
54-
54+
5555
// Obtain account properties
5656
$v_user = escapeshellarg($_SESSION[(($_SESSION['userContext'] === 'admin') && (isset($_SESSION['look']))) ? 'look' : 'user']);
5757

58-
exec (HESTIA_CMD . 'v-list-user ' . $v_user . ' json', $output, $return_var);
58+
exec(HESTIA_CMD . 'v-list-user ' . $v_user . ' json', $output, $return_var);
5959
$data = json_decode(implode('', $output), true);
60-
unset($output);
61-
60+
unset($output);
61+
6262
// Determine package features and land user at the first available page
6363
if ($data[$user]['WEB_DOMAINS'] !== '0') {
6464
header('Location: /list/web/');
@@ -87,40 +87,41 @@
8787
exit;
8888
}
8989

90-
function authenticate_user($user, $password, $twofa = ''){
90+
function authenticate_user($user, $password, $twofa = '')
91+
{
9192
unset($_SESSION['login']);
92-
if(isset($_SESSION['token']) && isset($_POST['token']) && $_POST['token'] == $_SESSION['token']) {
93-
$v_user = escapeshellarg($user);
94-
$ip = $_SERVER['REMOTE_ADDR'];
95-
$user_agent = $_SERVER['HTTP_USER_AGENT'];
96-
if(isset($_SERVER['HTTP_CF_CONNECTING_IP'])){
97-
if(!empty($_SERVER['HTTP_CF_CONNECTING_IP'])){
98-
$ip = $_SERVER['HTTP_CF_CONNECTING_IP'];
93+
if (isset($_SESSION['token']) && isset($_POST['token']) && $_POST['token'] == $_SESSION['token']) {
94+
$v_user = escapeshellarg($user);
95+
$ip = $_SERVER['REMOTE_ADDR'];
96+
$user_agent = $_SERVER['HTTP_USER_AGENT'];
97+
if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) {
98+
if (!empty($_SERVER['HTTP_CF_CONNECTING_IP'])) {
99+
$ip = $_SERVER['HTTP_CF_CONNECTING_IP'];
100+
}
99101
}
100-
}
101-
$v_ip = escapeshellarg($ip);
102-
$v_user_agent = escapeshellarg($user_agent);
102+
$v_ip = escapeshellarg($ip);
103+
$v_user_agent = escapeshellarg($user_agent);
103104

104-
// Get user's salt
105-
$output = '';
106-
exec (HESTIA_CMD . 'v-get-user-salt ' . $v_user . ' ' . $v_ip . ' json' , $output, $return_var);
107-
$pam = json_decode(implode('', $output), true);
108-
if ( $return_var > 0 ) {
109-
sleep(2);
110-
$error = '<a class="error">' . _('Invalid username or password') . '</a>';
111-
return $error;
105+
// Get user's salt
106+
$output = '';
107+
exec(HESTIA_CMD . 'v-get-user-salt ' . $v_user . ' ' . $v_ip . ' json', $output, $return_var);
108+
$pam = json_decode(implode('', $output), true);
109+
if ($return_var > 0) {
110+
sleep(2);
111+
$error = '<a class="error">' . _('Invalid username or password') . '</a>';
112+
return $error;
112113
} else {
113114
$salt = $pam[$user]['SALT'];
114115
$method = $pam[$user]['METHOD'];
115116

116-
if ($method == 'md5' ) {
117+
if ($method == 'md5') {
117118
$hash = crypt($password, '$1$' . $salt . '$');
118119
}
119-
if ($method == 'sha-512' ) {
120+
if ($method == 'sha-512') {
120121
$hash = crypt($password, '$6$rounds=5000$' . $salt . '$');
121122
$hash = str_replace('$rounds=5000', '', $hash);
122123
}
123-
if ($method == 'des' ) {
124+
if ($method == 'des') {
124125
$hash = crypt($password, $salt);
125126
}
126127

@@ -137,7 +138,7 @@ function authenticate_user($user, $password, $twofa = ''){
137138
// Remove tmp file
138139
unlink($v_hash);
139140
// Check API answer
140-
if ( $return_var > 0 ) {
141+
if ($return_var > 0) {
141142
sleep(2);
142143
$error = '<a class="error">' . _('Invalid username or password') . '</a>';
143144
$v_session_id = escapeshellarg($_POST['token']);
@@ -146,14 +147,14 @@ function authenticate_user($user, $password, $twofa = ''){
146147
} else {
147148

148149
// Get user specific parameters
149-
exec (HESTIA_CMD . 'v-list-user ' . $v_user . ' json', $output, $return_var);
150+
exec(HESTIA_CMD . 'v-list-user ' . $v_user . ' json', $output, $return_var);
150151
$data = json_decode(implode('', $output), true);
151-
unset($output);
152+
unset($output);
152153
if ($data[$user]['LOGIN_DISABLED'] === 'yes') {
153154
sleep(2);
154155
$error = '<a class="error">' . _('Invalid username or password') . '</a>';
155156
$v_session_id = escapeshellarg($_POST['token']);
156-
exec(HESTIA_CMD . 'v-log-user-login ' . $v_user . ' ' . $v_ip . ' failed ' . $v_session_id . ' ' . $v_user_agent, $output, $return_var);
157+
exec(HESTIA_CMD . 'v-log-user-login ' . $v_user . ' ' . $v_ip . ' failed ' . $v_session_id . ' ' . $v_user_agent .' yes "Login disabled for this user"', $output, $return_var);
157158
return $error;
158159
}
159160

@@ -164,35 +165,43 @@ function authenticate_user($user, $password, $twofa = ''){
164165
sleep(2);
165166
$error = '<a class="error">' . _('Invalid username or password') . '</a>';
166167
$v_session_id = escapeshellarg($_POST['token']);
167-
exec(HESTIA_CMD . 'v-log-user-login ' . $v_user . ' ' . $v_ip . ' failed ' . $v_session_id . ' ' . $v_user_agent, $output, $return_var);
168+
exec(HESTIA_CMD . 'v-log-user-login ' . $v_user . ' ' . $v_ip . ' failed ' . $v_session_id . ' ' . $v_user_agent .' yes "Ip not in allowed list"', $output, $return_var);
168169
return $error;
169170
}
170171
}
171172

172173
if ($data[$user]['TWOFA'] != '') {
173-
exec(HESTIA_CMD . "v-check-user-2fa " . $v_user . " " . $v_twofa, $output, $return_var);
174-
$error = "<a class=\"error\">" . _('Invalid or missing 2FA token') . "</a>";
175-
if(empty($twofa)){
174+
exec(HESTIA_CMD . "v-check-user-2fa " . $v_user . " " . $v_twofa, $output, $return_var);
175+
$error = "<a class=\"error\">" . _('Invalid or missing 2FA token') . "</a>";
176+
if (empty($twofa)) {
176177
$_SESSION['login']['username'] = $user;
177178
$_SESSION['login']['password'] = $password;
178179
return false;
179-
}else{
180+
} else {
180181
$v_twofa = escapeshellarg($twofa);
181182
exec(HESTIA_CMD .'v-check-user-2fa '.$v_user.' '.$v_twofa, $output, $return_var);
182183
unset($output);
183-
if ( $return_var > 0 ) {
184+
if ($return_var > 0) {
184185
sleep(2);
185-
$error = '<a class="error">' ._ ('Invalid or missing 2FA token') . '</a>';
186+
$error = '<a class="error">' ._('Invalid or missing 2FA token') . '</a>';
186187
$_SESSION['login']['username'] = $user;
187188
$_SESSION['login']['password'] = $password;
188189
$v_session_id = escapeshellarg($_POST['token']);
189-
exec(HESTIA_CMD.'v-log-user-login ' . $v_user . ' ' . $v_ip . ' failed ' . $v_session_id . ' ' . $v_user_agent, $output, $return_var);
190+
if (isset($_SESSION['failed_twofa'])) {
191+
//allow a few failed attemps before start of logging.
192+
if ($_SESSION['failed_twofa'] > 2) {
193+
exec(HESTIA_CMD.'v-log-user-login ' . $v_user . ' ' . $v_ip . ' failed ' . $v_session_id . ' ' . $v_user_agent .' yes "Invalid or missing 2FA token"', $output, $return_var);
194+
}
195+
$_SESSION['failed_twofa']++;
196+
} else {
197+
$_SESSION['failed_twofa'] = 1;
198+
}
190199
unset($_POST['twofa']);
191200
return $error;
192201
}
193202
}
194203
}
195-
204+
196205
// Define session user
197206
$_SESSION['user'] = key($data);
198207
$v_user = $_SESSION['user'];
@@ -216,10 +225,10 @@ function authenticate_user($user, $password, $twofa = ''){
216225

217226
// Define language
218227
$output = '';
219-
exec (HESTIA_CMD . 'v-list-sys-languages json', $output, $return_var);
228+
exec(HESTIA_CMD . 'v-list-sys-languages json', $output, $return_var);
220229
$languages = json_decode(implode('', $output), true);
221230
$_SESSION['language'] = (in_array($data[$v_user]['LANGUAGE'], $languages)) ? $data[$user]['LANGUAGE'] : 'en';
222-
231+
223232
// Regenerate session id to prevent session fixation
224233
session_regenerate_id(true);
225234

@@ -232,7 +241,7 @@ function authenticate_user($user, $password, $twofa = ''){
232241
if ($_SESSION['userContext'] === 'admin') {
233242
header('Location: /list/user/');
234243
} else {
235-
if($data[$user]['WEB_DOMAINS'] != '0') {
244+
if ($data[$user]['WEB_DOMAINS'] != '0') {
236245
header('Location: /list/web/');
237246
} elseif ($data[$user]['DNS_DOMAINS'] != '0') {
238247
header('Location: /list/dns/');
@@ -264,10 +273,10 @@ function authenticate_user($user, $password, $twofa = ''){
264273
return false;
265274
}
266275
}
267-
if (preg_match('/^[[:alnum:]][-|\.|_[:alnum:]]{0,28}[[:alnum:]]$/',$_POST['user'])) {
276+
if (preg_match('/^[[:alnum:]][-|\.|_[:alnum:]]{0,28}[[:alnum:]]$/', $_POST['user'])) {
268277
$_SESSION['login']['username'] = $_POST['user'];
269-
}else{
270-
$user = '';
278+
} else {
279+
$user = '';
271280
}
272281
if (!empty($_SESSION['login']['username']) && !empty($_SESSION['login']['password']) && !empty($_POST['twofa'])) {
273282
$error = authenticate_user($_SESSION['login']['username'], $_SESSION['login']['password'], $_POST['twofa']);
@@ -282,12 +291,12 @@ function authenticate_user($user, $password, $twofa = ''){
282291
// Detect language
283292
if (empty($_SESSION['language'])) {
284293
$output = '';
285-
exec (HESTIA_CMD . 'v-list-sys-config json', $output, $return_var);
294+
exec(HESTIA_CMD . 'v-list-sys-config json', $output, $return_var);
286295
$config = json_decode(implode('', $output), true);
287296
$lang = $config['config']['LANGUAGE'];
288297

289298
$output = '';
290-
exec (HESTIA_CMD . 'v-list-sys-languages json', $output, $return_var);
299+
exec(HESTIA_CMD . 'v-list-sys-languages json', $output, $return_var);
291300
$languages = json_decode(implode('', $output), true);
292301
$_SESSION['language'] = (in_array($lang, $languages)) ? $lang : 'en';
293302
}
@@ -302,7 +311,6 @@ function authenticate_user($user, $password, $twofa = ''){
302311
} elseif (empty($_SESSION['login']['username'])) {
303312
require_once('../templates/pages/login/login' . (($_SESSION['LOGIN_STYLE'] != 'old') ? '' : '_a') . '.html');
304313
} elseif (empty($_POST['password'])) {
305-
306314
require_once('../templates/pages/login/login_1.html');
307315
} else {
308316
require_once('../templates/pages/login/login' . (($_SESSION['LOGIN_STYLE'] != 'old') ? '' : '_a') . '.html');

0 commit comments

Comments
 (0)