From 2cfbce12fc0d75cdda07fc427c1636f00c988d56 Mon Sep 17 00:00:00 2001 From: Janusz Dobrowolski Date: Fri, 14 Sep 2012 23:49:49 +0200 Subject: [PATCH] Added prevention against brute force atacks on login page. --- access/login.php | 14 +++++++-- config.default.php | 8 ++++- includes/current_user.inc | 6 ++-- includes/session.inc | 61 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 5 deletions(-) diff --git a/access/login.php b/access/login.php index 24a74a1c..fe43ff4f 100644 --- a/access/login.php +++ b/access/login.php @@ -30,6 +30,16 @@ function defaultCompany() { $demo_text = _("Please login here"); } + + if (check_faillog()) + { + $blocked_msg = ''._('Too many failed login attempts.
Please wait a while or try later.').'
'; + + $js .= ""; + $demo_text = $blocked_msg; + } if (!isset($def_coy)) $def_coy = 0; $def_theme = "default"; @@ -100,12 +110,12 @@ function defaultCompany() text_row(_("Company"), "company_login_nickname", "", 20, 30); } start_row(); - label_cell($demo_text, "colspan=2 align='center'"); + label_cell($demo_text, "colspan=2 align='center' id='log_msg'"); end_row(); }; end_table(1); echo "
\n"; + .($login_timeout ? '':" onclick='set_fullmode();'").(isset($blocked_msg) ? " disabled" : '')." />\n"; foreach($_SESSION['timeout']['post'] as $p => $val) { // add all request variables to be resend together with login data diff --git a/config.default.php b/config.default.php index aed9cd9e..ae6668ef 100644 --- a/config.default.php +++ b/config.default.php @@ -253,4 +253,10 @@ $text_company_selection = false; $hide_inaccessible_menu_items = 0; -?> \ No newline at end of file +/* + Brute force prevention. + $login_delay seconds delay is required between login attempts after $login_max_attemps failed logins. + Set $login_delay to 0 to disable the feature (not recommended) +*/ +$login_delay = 30; +$login_max_attempts = 10; diff --git a/includes/current_user.inc b/includes/current_user.inc index 4c2aca30..512d09f0 100644 --- a/includes/current_user.inc +++ b/includes/current_user.inc @@ -18,7 +18,7 @@ if (!defined('TB_PREF')) { class current_user { - var $user; + var $user = 0; var $loginname; var $username; var $name; @@ -59,7 +59,7 @@ class current_user function login($company, $loginname, $password) { global $security_areas, $security_groups, $security_headings, $path_to_root; - + $this->set_company($company); $this->logged = false; @@ -73,6 +73,8 @@ class current_user if (!isset($Auth_Result)) // if not used: standard method $Auth_Result = get_user_auth($loginname, md5($password)); + write_login_filelog($loginname, $Auth_Result); + if ($Auth_Result) { $myrow = get_user_by_login($loginname); diff --git a/includes/session.inc b/includes/session.inc index 81e743c8..982616c4 100644 --- a/includes/session.inc +++ b/includes/session.inc @@ -140,6 +140,66 @@ function login_fail() kill_login(); die(); } +//---------------------------------------------------------------------------------------- +// set to reasonable values if not set in config file (pre-2.3.12 installations) + +if (!isset($login_delay)) +{ + $login_delay = 10; + $login_max_attempts = 3; +} + +function check_faillog() +{ + global $login_delay, $login_faillog, $login_max_attempts; + + $user = $_SESSION["wa_current_user"]->user; + + if (@$login_delay && ($login_faillog[$user][$_SERVER['REMOTE_ADDR']] >= @$login_max_attempts) && (time() < $login_faillog[$user]['last'] + $login_delay)) + return true; + + return false; +} +/* + Simple brute force attack detection is performed before connection to company database is open. Therefore access counters have to be stored in file. + Login attempts counter is created for every new user IP, which partialy prevent DOS attacks. +*/ +function write_login_filelog($login, $result) +{ + global $login_faillog, $login_max_attempts, $path_to_root; + + $user = $_SESSION["wa_current_user"]->user; + + $ip = $_SERVER['REMOTE_ADDR']; + + if (!isset($login_faillog[$user][$ip]) || $result) // init or reset on successfull login + $login_faillog[$user] = array($ip => 0, 'last' => ''); + + if (!$result) + { + if ($login_faillog[$user][$ip] < @$login_max_attempts) { + + $login_faillog[$user][$ip]++; + } else { + $login_faillog[$user][$ip] = 0; // comment out to restart counter only after successfull login. + error_log(sprintf(_("Brute force attack on account '%s' detected. Access for non-logged users temporarily blocked." ), $login)); + } + $login_faillog[$user]['last'] = time(); + } + + $msg = "