Bruteforce protection

This commit is contained in:
dignajar 2015-08-17 23:02:19 -03:00
parent 525e6f98c4
commit 9280cf3dfe
11 changed files with 83 additions and 37 deletions

View File

@ -8,6 +8,31 @@
// Functions // Functions
// ============================================================================ // ============================================================================
function checkPost($args)
{
global $Security;
global $Login;
global $Language;
if($Security->isBlocked()) {
Alert::set($Language->g('IP address has been blocked').'<br>'.$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 // Main before POST
// ============================================================================ // ============================================================================
@ -18,15 +43,7 @@
if( $_SERVER['REQUEST_METHOD'] == 'POST' ) if( $_SERVER['REQUEST_METHOD'] == 'POST' )
{ {
// Verify User sanitize the input checkPost($_POST);
if( $Login->verifyUser($_POST['username'], $_POST['password']) )
{
Redirect::page('admin', 'dashboard');
}
else
{
Alert::set($Language->g('Username or password incorrect'));
}
} }
// ============================================================================ // ============================================================================

View File

@ -16,6 +16,10 @@ div.unit-80 {
margin-left: 1% !important; margin-left: 1% !important;
} }
.tools-alert {
text-align: center;
}
/* ----------- FONTS AWESOME ----------- */ /* ----------- FONTS AWESOME ----------- */
.fa-right { .fa-right {
margin-right: 5px; margin-right: 5px;

View File

@ -2,7 +2,7 @@
<html> <html>
<head> <head>
<base href="<?php echo HTML_PATH_ADMIN_THEME ?>"> <base href="<?php echo HTML_PATH_ADMIN_THEME ?>">
<meta charset="utf-8"> <meta charset="<?php echo CHARSET ?>">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo $layout['title'] ?></title> <title><?php echo $layout['title'] ?></title>

View File

@ -2,10 +2,10 @@
<html> <html>
<head> <head>
<base href="<?php echo HTML_PATH_ADMIN_THEME ?>"> <base href="<?php echo HTML_PATH_ADMIN_THEME ?>">
<meta charset="utf-8"> <meta charset="<?php echo CHARSET ?>">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bludit Log in</title> <title>Bludit</title>
<link rel="stylesheet" href="./css/kube.min.css?version=<?php echo BLUDIT_VERSION ?>"> <link rel="stylesheet" href="./css/kube.min.css?version=<?php echo BLUDIT_VERSION ?>">
<link rel="stylesheet" href="./css/default.css?version=<?php echo BLUDIT_VERSION ?>"> <link rel="stylesheet" href="./css/default.css?version=<?php echo BLUDIT_VERSION ?>">
@ -23,7 +23,7 @@
<nav class="navbar nav-fullwidth"> <nav class="navbar nav-fullwidth">
<h1>Bludit</h1> <h1>Bludit</h1>
<ul> <ul>
<li><a href="<?php echo HTML_PATH_ROOT ?>"><?php $Language->p('Home') ?></a></li> <li><a href="<?php echo HTML_PATH_ROOT ?>"><?php $Language->p('Website') ?></a></li>
</ul> </ul>
</nav> </nav>
</div> </div>
@ -31,13 +31,13 @@
<div class="units-row"> <div class="units-row">
<!-- CONTENT --> <!-- CONTENT -->
<div class="unit-centered unit-40" style="max-width: 500px"> <div class="unit-centered unit-40" style="max-width: 400px">
<div id="content"> <div id="content">
<?php <?php
if(Alert::defined()) { if(Alert::defined()) {
echo '<div class="tools-alert tools-alert-red">'.Alert::get().'</div>'; echo '<div class="tools-alert tools-alert-green">'.Alert::get().'</div>';
} }
// Load view // Load view
@ -51,8 +51,6 @@
</div> </div>
<div id="footer">Bludit</div>
<!-- Plugins Login Body Begin --> <!-- Plugins Login Body Begin -->
<?php Theme::plugins('loginBodyEnd') ?> <?php Theme::plugins('loginBodyEnd') ?>

View File

@ -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_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 // File plugins/pages/db.php
$data = array( $data = array(
'homeLink'=>true, 'homeLink'=>true,
@ -364,7 +374,7 @@ if( $_SERVER['REQUEST_METHOD'] == 'POST' )
<html lang="en"> <html lang="en">
<head> <head>
<base href="admin/themes/default/"> <base href="admin/themes/default/">
<meta charset="utf-8"> <meta charset="<?php echo CHARSET ?>">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo $Language->get('Bludit Installer') ?></title> <title><?php echo $Language->get('Bludit Installer') ?></title>
@ -390,7 +400,7 @@ if( $_SERVER['REQUEST_METHOD'] == 'POST' )
// Missing requirements // Missing requirements
if(!empty($system)) if(!empty($system))
{ {
echo '<div class="unit-centered unit-50">'; echo '<div class="boxInstallerForm unit-centered unit-50">';
echo '<table class="table-stripped">'; echo '<table class="table-stripped">';
foreach($system as $value) { foreach($system as $value) {

View File

@ -25,16 +25,16 @@ if ( in_array( strtolower( ini_get( 'magic_quotes_gpc' ) ), array( '1', 'on' ) )
} }
// AJAX // AJAX
if( $Login->isLogged() && ($layout['slug']==='ajax') ) if( $layout['slug']==='ajax' )
{
if($Login->isLogged())
{ {
// Boot rules
// Ajax doesn't load rules
// Load AJAX file // Load AJAX file
if( Sanitize::pathFile(PATH_AJAX, $layout['parameters'].'.php') ) { if( Sanitize::pathFile(PATH_AJAX, $layout['parameters'].'.php') ) {
include(PATH_AJAX.$layout['parameters'].'.php'); include(PATH_AJAX.$layout['parameters'].'.php');
} }
} }
}
// ADMIN AREA // ADMIN AREA
else else
{ {

View File

@ -97,6 +97,7 @@ include(PATH_KERNEL.'page.class.php');
include(PATH_KERNEL.'url.class.php'); include(PATH_KERNEL.'url.class.php');
include(PATH_KERNEL.'login.class.php'); include(PATH_KERNEL.'login.class.php');
include(PATH_KERNEL.'parsedown.class.php'); include(PATH_KERNEL.'parsedown.class.php');
include(PATH_KERNEL.'security.class.php');
// Include Helpers Classes // Include Helpers Classes
include(PATH_HELPERS.'text.class.php'); include(PATH_HELPERS.'text.class.php');
@ -125,6 +126,7 @@ $dbUsers = new dbUsers();
$Site = new dbSite(); $Site = new dbSite();
$Url = new Url(); $Url = new Url();
$Parsedown = new Parsedown(); $Parsedown = new Parsedown();
$Security = new Security();
// HTML PATHs // HTML PATHs
$base = (dirname(getenv('SCRIPT_NAME'))==DS)?'/':dirname(getenv('SCRIPT_NAME')).'/'; $base = (dirname(getenv('SCRIPT_NAME'))==DS)?'/':dirname(getenv('SCRIPT_NAME')).'/';

View File

@ -65,7 +65,7 @@ class Login {
$user = $this->dbUsers->getDb($username); $user = $this->dbUsers->getDb($username);
if($user==false) { if($user==false) {
Log::set(__METHOD__.LOG_SEP.'Username not exist: '.$username); Log::set(__METHOD__.LOG_SEP.'Username does not exist: '.$username);
return false; return false;
} }
@ -78,7 +78,7 @@ class Login {
return true; return true;
} }
else { else {
Log::set(__METHOD__.LOG_SEP.'Password are differents.'); Log::set(__METHOD__.LOG_SEP.'Password incorrect.');
} }
return false; return false;

View File

@ -5,7 +5,7 @@ class Security extends dbJSON
private $dbFields = array( private $dbFields = array(
'minutesBlocked'=>5, 'minutesBlocked'=>5,
'numberFailuresAllowed'=>10, 'numberFailuresAllowed'=>10,
'blackList'=>array('numberFailures', 'lastFailure') 'blackList'=>array()
); );
function __construct() function __construct()
@ -27,12 +27,13 @@ class Security extends dbJSON
$lastFailure = $userBlack['lastFailure']; $lastFailure = $userBlack['lastFailure'];
// Check if the IP is expired, then is not blocked. // 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; return false;
} }
// The IP has more failures than number of failures, then the IP is blocked. // The IP has more failures than number of failures, then the IP is blocked.
if($numberFailures >= $this->db['numberFailuresAllowed']) { if($numberFailures >= $this->db['numberFailuresAllowed']) {
Log::set(__METHOD__.LOG_SEP.'IP Blocked:'.$ip);
return true; return true;
} }
@ -46,13 +47,23 @@ class Security extends dbJSON
$currentTime = time(); $currentTime = time();
$numberFailures = 1; $numberFailures = 1;
if(isset($this->db['blackList'][$ip])) { 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 = $userBlack['numberFailures'];
$numberFailures = $numberFailures + 1; $numberFailures = $numberFailures + 1;
} }
}
$this->db['blackList'][$ip] = array('lastFailure'=>$currentTime, 'numberFailures'=>$numberFailures); $this->db['blackList'][$ip] = array('lastFailure'=>$currentTime, 'numberFailures'=>$numberFailures);
Log::set(__METHOD__.LOG_SEP.'Blacklist, IP:'.$ip.', Number of failures:'.$numberFailures);
// Save the database // Save the database
if( $this->save() === false ) { if( $this->save() === false ) {
Log::set(__METHOD__.LOG_SEP.'Error occurred when trying to save the database file.'); Log::set(__METHOD__.LOG_SEP.'Error occurred when trying to save the database file.');

View File

@ -155,5 +155,7 @@
"the-password-field-is-empty": "The password field is empty", "the-password-field-is-empty": "The password field is empty",
"your-email-address-is-invalid":"Your email address is invalid.", "your-email-address-is-invalid":"Your email address is invalid.",
"proceed-anyway": "Proceed anyway!", "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."
} }

View File

@ -153,5 +153,7 @@
"the-password-field-is-empty": "Debe completar el campo contraseña", "the-password-field-is-empty": "Debe completar el campo contraseña",
"your-email-address-is-invalid":"Su dirección de correo es invalida.", "your-email-address-is-invalid":"Su dirección de correo es invalida.",
"proceed-anyway": "Continuar de todas formas!", "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."
} }