From 9280cf3dfea8f154aa8cf78629eb642e0a165f0c Mon Sep 17 00:00:00 2001 From: dignajar Date: Mon, 17 Aug 2015 23:02:19 -0300 Subject: [PATCH] Bruteforce protection --- admin/controllers/login.php | 35 +++++++++++++++++++++------- admin/themes/default/css/default.css | 4 ++++ admin/themes/default/index.php | 2 +- admin/themes/default/login.php | 12 ++++------ install.php | 14 +++++++++-- kernel/boot/admin.php | 16 ++++++------- kernel/boot/init.php | 2 ++ kernel/login.class.php | 6 ++--- kernel/security.class.php | 21 +++++++++++++---- languages/en_US.json | 4 +++- languages/es_AR.json | 4 +++- 11 files changed, 83 insertions(+), 37 deletions(-) diff --git a/admin/controllers/login.php b/admin/controllers/login.php index 60227b2e..d9ee03fe 100644 --- a/admin/controllers/login.php +++ b/admin/controllers/login.php @@ -8,6 +8,31 @@ // Functions // ============================================================================ +function checkPost($args) +{ + global $Security; + global $Login; + global $Language; + + if($Security->isBlocked()) { + Alert::set($Language->g('IP address has been blocked').'
'.$Language->g('Try again in a few minutes')); + return false; + } + + // Verify User sanitize the input + if( $Login->verifyUser($_POST['username'], $_POST['password']) ) + { + Redirect::page('admin', 'dashboard'); + return true; + } + + // Bruteforce protection, add IP to blacklist. + $Security->addLoginFail(); + Alert::set($Language->g('Username or password incorrect')); + + return false; +} + // ============================================================================ // Main before POST // ============================================================================ @@ -18,15 +43,7 @@ if( $_SERVER['REQUEST_METHOD'] == 'POST' ) { - // Verify User sanitize the input - if( $Login->verifyUser($_POST['username'], $_POST['password']) ) - { - Redirect::page('admin', 'dashboard'); - } - else - { - Alert::set($Language->g('Username or password incorrect')); - } + checkPost($_POST); } // ============================================================================ diff --git a/admin/themes/default/css/default.css b/admin/themes/default/css/default.css index b375a66d..5246859e 100644 --- a/admin/themes/default/css/default.css +++ b/admin/themes/default/css/default.css @@ -16,6 +16,10 @@ div.unit-80 { margin-left: 1% !important; } +.tools-alert { + text-align: center; +} + /* ----------- FONTS AWESOME ----------- */ .fa-right { margin-right: 5px; diff --git a/admin/themes/default/index.php b/admin/themes/default/index.php index 02113bb6..c6738ce8 100644 --- a/admin/themes/default/index.php +++ b/admin/themes/default/index.php @@ -2,7 +2,7 @@ - + <?php echo $layout['title'] ?> diff --git a/admin/themes/default/login.php b/admin/themes/default/login.php index d4744cd0..e7cf297f 100644 --- a/admin/themes/default/login.php +++ b/admin/themes/default/login.php @@ -2,10 +2,10 @@ - + - Bludit Log in + Bludit @@ -23,7 +23,7 @@ @@ -31,13 +31,13 @@
-
+
'.Alert::get().'
'; + echo '
'.Alert::get().'
'; } // Load view @@ -51,8 +51,6 @@
- - diff --git a/install.php b/install.php index d5e6fda8..6c937ad6 100755 --- a/install.php +++ b/install.php @@ -273,6 +273,16 @@ function install($adminPassword, $email) file_put_contents(PATH_DATABASES.'users.php', $dataHead.json_encode($data, JSON_PRETTY_PRINT), LOCK_EX); + // File security.php + $data = array( + 'minutesBlocked'=>5, + 'numberFailuresAllowed'=>10, + 'blackList'=>array() + ); + + file_put_contents(PATH_DATABASES.'security.php', $dataHead.json_encode($data, JSON_PRETTY_PRINT), LOCK_EX); + + // File plugins/pages/db.php $data = array( 'homeLink'=>true, @@ -364,7 +374,7 @@ if( $_SERVER['REQUEST_METHOD'] == 'POST' ) - + <?php echo $Language->get('Bludit Installer') ?> @@ -390,7 +400,7 @@ if( $_SERVER['REQUEST_METHOD'] == 'POST' ) // Missing requirements if(!empty($system)) { - echo '
'; + echo '
'; echo ''; foreach($system as $value) { diff --git a/kernel/boot/admin.php b/kernel/boot/admin.php index 954de147..3f7e2719 100644 --- a/kernel/boot/admin.php +++ b/kernel/boot/admin.php @@ -25,14 +25,14 @@ if ( in_array( strtolower( ini_get( 'magic_quotes_gpc' ) ), array( '1', 'on' ) ) } // AJAX -if( $Login->isLogged() && ($layout['slug']==='ajax') ) +if( $layout['slug']==='ajax' ) { - // Boot rules - // Ajax doesn't load rules - - // Load AJAX file - if( Sanitize::pathFile(PATH_AJAX, $layout['parameters'].'.php') ) { - include(PATH_AJAX.$layout['parameters'].'.php'); + if($Login->isLogged()) + { + // Load AJAX file + if( Sanitize::pathFile(PATH_AJAX, $layout['parameters'].'.php') ) { + include(PATH_AJAX.$layout['parameters'].'.php'); + } } } // ADMIN AREA @@ -73,4 +73,4 @@ else // Plugins after admin area loaded Theme::plugins('afterAdminLoad'); -} +} \ No newline at end of file diff --git a/kernel/boot/init.php b/kernel/boot/init.php index 09814485..afd5c70a 100644 --- a/kernel/boot/init.php +++ b/kernel/boot/init.php @@ -97,6 +97,7 @@ include(PATH_KERNEL.'page.class.php'); include(PATH_KERNEL.'url.class.php'); include(PATH_KERNEL.'login.class.php'); include(PATH_KERNEL.'parsedown.class.php'); +include(PATH_KERNEL.'security.class.php'); // Include Helpers Classes include(PATH_HELPERS.'text.class.php'); @@ -125,6 +126,7 @@ $dbUsers = new dbUsers(); $Site = new dbSite(); $Url = new Url(); $Parsedown = new Parsedown(); +$Security = new Security(); // HTML PATHs $base = (dirname(getenv('SCRIPT_NAME'))==DS)?'/':dirname(getenv('SCRIPT_NAME')).'/'; diff --git a/kernel/login.class.php b/kernel/login.class.php index d55d118b..476c09cb 100644 --- a/kernel/login.class.php +++ b/kernel/login.class.php @@ -65,7 +65,7 @@ class Login { $user = $this->dbUsers->getDb($username); if($user==false) { - Log::set(__METHOD__.LOG_SEP.'Username not exist: '.$username); + Log::set(__METHOD__.LOG_SEP.'Username does not exist: '.$username); return false; } @@ -78,7 +78,7 @@ class Login { return true; } else { - Log::set(__METHOD__.LOG_SEP.'Password are differents.'); + Log::set(__METHOD__.LOG_SEP.'Password incorrect.'); } return false; @@ -113,4 +113,4 @@ class Login { return Session::destroy(); } -} +} \ No newline at end of file diff --git a/kernel/security.class.php b/kernel/security.class.php index c4ac5a34..7fe3be21 100644 --- a/kernel/security.class.php +++ b/kernel/security.class.php @@ -5,7 +5,7 @@ class Security extends dbJSON private $dbFields = array( 'minutesBlocked'=>5, 'numberFailuresAllowed'=>10, - 'blackList'=>array('numberFailures', 'lastFailure') + 'blackList'=>array() ); function __construct() @@ -27,12 +27,13 @@ class Security extends dbJSON $lastFailure = $userBlack['lastFailure']; // Check if the IP is expired, then is not blocked. - if($currentTime > $lastFailure + $this->db['minutesBlocked']) { + if($currentTime > $lastFailure + ($this->db['minutesBlocked']*60)) { return false; } // The IP has more failures than number of failures, then the IP is blocked. if($numberFailures >= $this->db['numberFailuresAllowed']) { + Log::set(__METHOD__.LOG_SEP.'IP Blocked:'.$ip); return true; } @@ -46,13 +47,23 @@ class Security extends dbJSON $currentTime = time(); $numberFailures = 1; - if(isset($this->db['blackList'][$ip])) { - $numberFailures = $userBlack['numberFailures']; - $numberFailures = $numberFailures + 1; + if(isset($this->db['blackList'][$ip])) + { + $userBlack = $this->db['blackList'][$ip]; + $lastFailure = $userBlack['lastFailure']; + + // Check if the IP is expired, then renew the number of failures. + if($currentTime <= $lastFailure + ($this->db['minutesBlocked']*60)) + { + $numberFailures = $userBlack['numberFailures']; + $numberFailures = $numberFailures + 1; + } } $this->db['blackList'][$ip] = array('lastFailure'=>$currentTime, 'numberFailures'=>$numberFailures); + Log::set(__METHOD__.LOG_SEP.'Blacklist, IP:'.$ip.', Number of failures:'.$numberFailures); + // Save the database if( $this->save() === false ) { Log::set(__METHOD__.LOG_SEP.'Error occurred when trying to save the database file.'); diff --git a/languages/en_US.json b/languages/en_US.json index 6fdec683..23d306a8 100755 --- a/languages/en_US.json +++ b/languages/en_US.json @@ -155,5 +155,7 @@ "the-password-field-is-empty": "The password field is empty", "your-email-address-is-invalid":"Your email address is invalid.", "proceed-anyway": "Proceed anyway!", - "drafts":"Drafts" + "drafts":"Drafts", + "ip-address-has-been-blocked": "IP address has been blocked.", + "try-again-in-a-few-minutes": "Try again in a few minutes." } \ No newline at end of file diff --git a/languages/es_AR.json b/languages/es_AR.json index 8c8d6dc1..6d5643ab 100755 --- a/languages/es_AR.json +++ b/languages/es_AR.json @@ -153,5 +153,7 @@ "the-password-field-is-empty": "Debe completar el campo contraseña", "your-email-address-is-invalid":"Su dirección de correo es invalida.", "proceed-anyway": "Continuar de todas formas!", - "drafts":"Borradores" + "drafts":"Borradores", + "ip-address-has-been-blocked":"La direccion IP fue bloqueada.", + "try-again-in-a-few-minutes": "Vuelva a intentar en unos minutos." } \ No newline at end of file