Login and Security improves

This commit is contained in:
Diego Najar 2017-07-16 00:42:37 +02:00
parent 0be75f22c4
commit 7f8e012486
13 changed files with 76 additions and 127 deletions

View File

@ -91,7 +91,7 @@ function checkGet($args)
}
// Bruteforce protection, add IP to blacklist.
$Security->addLoginFail();
$Security->addToBlacklist();
return false;
}

View File

@ -8,7 +8,7 @@
// Functions
// ============================================================================
function checkPost($args)
function checkLogin($args)
{
global $Security;
global $Login;
@ -19,7 +19,6 @@ function checkPost($args)
return false;
}
// Verify User sanitize the input
if ($Login->verifyUser($_POST['username'], $_POST['password'])) {
// Renew the token. This token will be the same inside the session for multiple forms.
$Security->generateTokenCSRF();
@ -27,8 +26,8 @@ function checkPost($args)
return true;
}
// Bruteforce protection, add IP to blacklist.
$Security->addLoginFail();
// Bruteforce protection, add IP to the blacklist
$Security->addToBlacklist();
// Create alert
Alert::set($Language->g('Username or password incorrect'));
@ -46,7 +45,7 @@ function checkPost($args)
if ($_SERVER['REQUEST_METHOD']=='POST')
{
checkPost($_POST);
checkLogin($_POST);
}
// ============================================================================

View File

@ -144,6 +144,9 @@ $(document).ready(function() {
<?php
if( Sanitize::pathFile(PATH_ADMIN_VIEWS, $layout['view'].'.php') ) {
include(PATH_ADMIN_VIEWS.$layout['view'].'.php');
} else {
echo '<h1 style="width:100%; text-align:center">Hey!</h1>';
echo '<h2 style="width:100%; text-align:center">Have you seen my ball?</h2>';
}
?>
</div>

View File

@ -4,7 +4,7 @@
<form method="post" action="" class="uk-form" autocomplete="off">
<input type="hidden" id="jstoken" name="tokenCSRF" value="<?php $Security->printTokenCSRF() ?>">
<input type="hidden" id="jstoken" name="tokenCSRF" value="<?php echo $Security->getTokenCSRF() ?>">
<div class="uk-form-row">
<input name="email" class="uk-width-1-1 uk-form-large" placeholder="<?php $L->p('Email') ?>" type="text">

View File

@ -2,7 +2,7 @@
<form method="post" action="" class="uk-form" autocomplete="off">
<input type="hidden" id="jstoken" name="tokenCSRF" value="<?php $Security->printTokenCSRF() ?>">
<input type="hidden" id="jstoken" name="tokenCSRF" value="<?php echo $Security->getTokenCSRF() ?>">
<div class="uk-form-row">
<input name="username" class="uk-width-1-1 uk-form-large" placeholder="<?php $L->p('Username') ?>" type="text">

View File

@ -50,14 +50,12 @@ else
// User not logged.
// Slug is login.
// Slug is login-email.
if($Url->notFound() || !$Login->isLogged() || ($Url->slug()==='login') || ($Url->slug()==='login-email') )
{
if($Url->notFound() || !$Login->isLogged() || ($Url->slug()==='login') || ($Url->slug()==='login-email') ) {
$layout['controller'] = 'login';
$layout['view'] = 'login';
$layout['template'] = 'login.php';
if($Url->slug()==='login-email')
{
if ($Url->slug()==='login-email') {
$layout['controller'] = 'login-email';
$layout['view'] = 'login-email';
}

View File

@ -57,6 +57,7 @@ define('DB_CATEGORIES', PATH_DATABASES.'categories.php');
define('DB_TAGS', PATH_DATABASES.'tags.php');
define('DB_SYSLOG', PATH_DATABASES.'syslog.php');
define('DB_USERS', PATH_DATABASES.'users.php');
define('DB_SECURITY', PATH_DATABASES.'security.php');
// Log separator
define('LOG_SEP', ' | ');

View File

@ -185,19 +185,18 @@ class dbUsers extends dbJSON
return $token;
}
// ---- OLD
// Returns array with the username databases filtered by username, FALSE otherwise
public function getDb($username)
public function getDB($username)
{
if ($this->exists($username)) {
$user = $this->db[$username];
return $user;
return $this->db[$username];
}
return false;
}
// ---- OLD
public function getAll()
{
return $this->db;

View File

@ -55,13 +55,9 @@
public static function destroy()
{
session_destroy();
unset($_SESSION);
self::$started = false;
Log::set(__METHOD__.LOG_SEP.'Session destroyed.');
return !isset($_SESSION);
}

View File

@ -19,6 +19,26 @@ class Login {
return Session::get('role');
}
// Returns TRUE if the user is logged, FALSE otherwise
public function isLogged()
{
if (Session::get('fingerPrint')===$this->fingerPrint()) {
$username = Session::get('username');
if (!empty($username)) {
return true;
}
else {
Log::set(__METHOD__.LOG_SEP.'Session username empty, destroy the session.');
Session::destroy();
return false;
}
}
Log::set(__METHOD__.LOG_SEP.'FingerPrint are differents. Current fingerPrint: '.Session::get('fingerPrint').' !== Current fingerPrint: '.$this->fingerPrint());
return false;
}
// Set the session for the user logged
public function setLogin($username, $role)
{
Session::set('username', $username);
@ -26,30 +46,12 @@ class Login {
Session::set('fingerPrint', $this->fingerPrint());
Session::set('sessionTime', time());
Log::set(__METHOD__.LOG_SEP.'Set fingerPrint: '.$this->fingerPrint());
}
public function isLogged()
{
if(Session::get('fingerPrint')===$this->fingerPrint())
{
$username = Session::get('username');
if(!empty($username)) {
return true;
}
else {
Log::set(__METHOD__.LOG_SEP.'Session username empty: '.$username);
}
}
else
{
Log::set(__METHOD__.LOG_SEP.'FingerPrint are differents. Session fingerPrint: '.Session::get('fingerPrint').' !== Current fingerPrint: '.$this->fingerPrint());
}
return false;
Log::set(__METHOD__.LOG_SEP.'User logged, fingerprint: '.$this->fingerPrint());
}
// Check if the username and the password are valid
// Returns TRUE if valid and set the session
// Returns FALSE for invalid username or password
public function verifyUser($username, $password)
{
$username = Sanitize::html($username);
@ -63,20 +65,16 @@ class Login {
return false;
}
$user = $this->dbUsers->getDb($username);
$user = $this->dbUsers->getDB($username);
if($user==false) {
Log::set(__METHOD__.LOG_SEP.'Username does not exist: '.$username);
return false;
}
$passwordHash = sha1($password.$user['salt']);
if($passwordHash === $user['password'])
{
$passwordHash = $this->dbUsers->generatePasswordHash($password, $user['salt']);
if ($passwordHash===$user['password']) {
$this->setLogin($username, $user['role']);
Log::set(__METHOD__.LOG_SEP.'User logged succeeded by username and password - Username: '.$username);
return true;
}
else {
@ -130,27 +128,14 @@ class Login {
return false;
}
public function fingerPrint($random=false)
public function fingerPrint()
{
// User agent
$agent = getenv('HTTP_USER_AGENT');
if (empty($agent)) {
$agent = 'Bludit/1.0 (Mr Nibbler Protocol)';
$agent = 'Bludit/2.0 (Mr Nibbler Protocol)';
}
// User IP
if(getenv('HTTP_X_FORWARDED_FOR'))
$ip = getenv('HTTP_X_FORWARDED_FOR');
elseif(getenv('HTTP_CLIENT_IP'))
$ip = getenv('HTTP_CLIENT_IP');
else
$ip = getenv('REMOTE_ADDR');
if($random) {
return sha1(mt_rand().$agent.$ip);
}
// DEBUG: Ver CLIENT IP, hay veces que retorna la ip ::1 y otras 127.0.0.1
return sha1($agent);
}

View File

@ -1,9 +1,8 @@
<?php defined('BLUDIT') or die('Bludit CMS.');
<?php defined('BLUDIT') or die('Bludit Badass CMS.');
class Security extends dbJSON
{
private $dbFields = array(
'key1'=>'Where we go we dont need roads',
'minutesBlocked'=>5,
'numberFailuresAllowed'=>10,
'blackList'=>array()
@ -11,51 +10,33 @@ class Security extends dbJSON
function __construct()
{
parent::__construct(PATH_DATABASES.'security.php');
parent::__construct(DB_SECURITY);
}
// Authentication key
// --------------------------------------------------------------------
public function key1()
{
return $this->db['key1'];
}
// ====================================================
// TOKEN FOR CSRF
// ====================================================
// Generate and save the token in Session.
// Generate and save the token in Session
public function generateTokenCSRF()
{
$token = Text::randomText(8);
$token = sha1($token);
Log::set(__METHOD__.LOG_SEP.'New tokenCSRF was generated '.$token);
$token = sha1( uniqid().time() );
Session::set('tokenCSRF', $token);
}
// Validate the token.
// Validate the token
public function validateTokenCSRF($token)
{
$sessionToken = Session::get('tokenCSRF');
$sessionToken = $this->getTokenCSRF();
return ( !empty($sessionToken) && ($sessionToken===$token) );
}
// Returns the token.
// Returns the token
public function getTokenCSRF()
{
return Session::get('tokenCSRF');
}
public function printTokenCSRF()
{
echo Session::get('tokenCSRF');
}
// ====================================================
// BRUTE FORCE PROTECTION
// ====================================================
@ -73,51 +54,42 @@ class Security extends dbJSON
$numberFailures = $userBlack['numberFailures'];
$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']*60)) {
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']) {
Log::set(__METHOD__.LOG_SEP.'IP Blocked:'.$ip);
return true;
}
// Otherwise the IP is not blocked.
// Otherwise the IP is not blocked
return false;
}
public function addLoginFail()
// Add or update the current client IP on the blacklist
public function addToBlacklist()
{
$ip = $this->getUserIp();
$currentTime = time();
$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))
{
// 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.');
return false;
}
return true;
return $this->save();
}
public function getNumberFailures($ip=null)
@ -134,14 +106,13 @@ class Security extends dbJSON
public function getUserIp()
{
// User IP
if(getenv('HTTP_X_FORWARDED_FOR'))
if (getenv('HTTP_X_FORWARDED_FOR')) {
$ip = getenv('HTTP_X_FORWARDED_FOR');
elseif(getenv('HTTP_CLIENT_IP'))
} elseif (getenv('HTTP_CLIENT_IP')) {
$ip = getenv('HTTP_CLIENT_IP');
else
} else {
$ip = getenv('REMOTE_ADDR');
}
return $ip;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -418,10 +418,7 @@ function install($adminPassword, $email, $timezone)
file_put_contents(PATH_DATABASES.'syslog.php', $dataHead.json_encode($data, JSON_PRETTY_PRINT), LOCK_EX);
// File security.php
$randomKey = sha1( uniqid() );
$data = array(
'key1'=>$randomKey,
'minutesBlocked'=>5,
'numberFailuresAllowed'=>10,
'blackList'=>array()