Login and Security improves
This commit is contained in:
parent
0be75f22c4
commit
7f8e012486
|
@ -91,7 +91,7 @@ function checkGet($args)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bruteforce protection, add IP to blacklist.
|
// Bruteforce protection, add IP to blacklist.
|
||||||
$Security->addLoginFail();
|
$Security->addToBlacklist();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
// Functions
|
// Functions
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
function checkPost($args)
|
function checkLogin($args)
|
||||||
{
|
{
|
||||||
global $Security;
|
global $Security;
|
||||||
global $Login;
|
global $Login;
|
||||||
|
@ -19,7 +19,6 @@ function checkPost($args)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify User sanitize the input
|
|
||||||
if ($Login->verifyUser($_POST['username'], $_POST['password'])) {
|
if ($Login->verifyUser($_POST['username'], $_POST['password'])) {
|
||||||
// Renew the token. This token will be the same inside the session for multiple forms.
|
// Renew the token. This token will be the same inside the session for multiple forms.
|
||||||
$Security->generateTokenCSRF();
|
$Security->generateTokenCSRF();
|
||||||
|
@ -27,8 +26,8 @@ function checkPost($args)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bruteforce protection, add IP to blacklist.
|
// Bruteforce protection, add IP to the blacklist
|
||||||
$Security->addLoginFail();
|
$Security->addToBlacklist();
|
||||||
|
|
||||||
// Create alert
|
// Create alert
|
||||||
Alert::set($Language->g('Username or password incorrect'));
|
Alert::set($Language->g('Username or password incorrect'));
|
||||||
|
@ -46,7 +45,7 @@ function checkPost($args)
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD']=='POST')
|
if ($_SERVER['REQUEST_METHOD']=='POST')
|
||||||
{
|
{
|
||||||
checkPost($_POST);
|
checkLogin($_POST);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
|
@ -144,6 +144,9 @@ $(document).ready(function() {
|
||||||
<?php
|
<?php
|
||||||
if( Sanitize::pathFile(PATH_ADMIN_VIEWS, $layout['view'].'.php') ) {
|
if( Sanitize::pathFile(PATH_ADMIN_VIEWS, $layout['view'].'.php') ) {
|
||||||
include(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>
|
</div>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
<form method="post" action="" class="uk-form" autocomplete="off">
|
<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">
|
<div class="uk-form-row">
|
||||||
<input name="email" class="uk-width-1-1 uk-form-large" placeholder="<?php $L->p('Email') ?>" type="text">
|
<input name="email" class="uk-width-1-1 uk-form-large" placeholder="<?php $L->p('Email') ?>" type="text">
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<form method="post" action="" class="uk-form" autocomplete="off">
|
<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">
|
<div class="uk-form-row">
|
||||||
<input name="username" class="uk-width-1-1 uk-form-large" placeholder="<?php $L->p('Username') ?>" type="text">
|
<input name="username" class="uk-width-1-1 uk-form-large" placeholder="<?php $L->p('Username') ?>" type="text">
|
||||||
|
|
|
@ -50,14 +50,12 @@ else
|
||||||
// User not logged.
|
// User not logged.
|
||||||
// Slug is login.
|
// Slug is login.
|
||||||
// Slug is login-email.
|
// 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['controller'] = 'login';
|
||||||
$layout['view'] = 'login';
|
$layout['view'] = 'login';
|
||||||
$layout['template'] = 'login.php';
|
$layout['template'] = 'login.php';
|
||||||
|
|
||||||
if($Url->slug()==='login-email')
|
if ($Url->slug()==='login-email') {
|
||||||
{
|
|
||||||
$layout['controller'] = 'login-email';
|
$layout['controller'] = 'login-email';
|
||||||
$layout['view'] = 'login-email';
|
$layout['view'] = 'login-email';
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,7 @@ define('DB_CATEGORIES', PATH_DATABASES.'categories.php');
|
||||||
define('DB_TAGS', PATH_DATABASES.'tags.php');
|
define('DB_TAGS', PATH_DATABASES.'tags.php');
|
||||||
define('DB_SYSLOG', PATH_DATABASES.'syslog.php');
|
define('DB_SYSLOG', PATH_DATABASES.'syslog.php');
|
||||||
define('DB_USERS', PATH_DATABASES.'users.php');
|
define('DB_USERS', PATH_DATABASES.'users.php');
|
||||||
|
define('DB_SECURITY', PATH_DATABASES.'security.php');
|
||||||
|
|
||||||
// Log separator
|
// Log separator
|
||||||
define('LOG_SEP', ' | ');
|
define('LOG_SEP', ' | ');
|
||||||
|
|
|
@ -185,19 +185,18 @@ class dbUsers extends dbJSON
|
||||||
return $token;
|
return $token;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- OLD
|
|
||||||
// Returns array with the username databases filtered by username, FALSE otherwise
|
// Returns array with the username databases filtered by username, FALSE otherwise
|
||||||
public function getDb($username)
|
public function getDB($username)
|
||||||
{
|
{
|
||||||
if ($this->exists($username)) {
|
if ($this->exists($username)) {
|
||||||
$user = $this->db[$username];
|
return $this->db[$username];
|
||||||
|
|
||||||
return $user;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- OLD
|
||||||
|
|
||||||
|
|
||||||
public function getAll()
|
public function getAll()
|
||||||
{
|
{
|
||||||
return $this->db;
|
return $this->db;
|
||||||
|
|
|
@ -55,13 +55,9 @@
|
||||||
public static function destroy()
|
public static function destroy()
|
||||||
{
|
{
|
||||||
session_destroy();
|
session_destroy();
|
||||||
|
|
||||||
unset($_SESSION);
|
unset($_SESSION);
|
||||||
|
|
||||||
self::$started = false;
|
self::$started = false;
|
||||||
|
|
||||||
Log::set(__METHOD__.LOG_SEP.'Session destroyed.');
|
Log::set(__METHOD__.LOG_SEP.'Session destroyed.');
|
||||||
|
|
||||||
return !isset($_SESSION);
|
return !isset($_SESSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,26 @@ class Login {
|
||||||
return Session::get('role');
|
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)
|
public function setLogin($username, $role)
|
||||||
{
|
{
|
||||||
Session::set('username', $username);
|
Session::set('username', $username);
|
||||||
|
@ -26,30 +46,12 @@ class Login {
|
||||||
Session::set('fingerPrint', $this->fingerPrint());
|
Session::set('fingerPrint', $this->fingerPrint());
|
||||||
Session::set('sessionTime', time());
|
Session::set('sessionTime', time());
|
||||||
|
|
||||||
Log::set(__METHOD__.LOG_SEP.'Set fingerPrint: '.$this->fingerPrint());
|
Log::set(__METHOD__.LOG_SEP.'User logged, 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)
|
public function verifyUser($username, $password)
|
||||||
{
|
{
|
||||||
$username = Sanitize::html($username);
|
$username = Sanitize::html($username);
|
||||||
|
@ -63,20 +65,16 @@ class Login {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = $this->dbUsers->getDb($username);
|
$user = $this->dbUsers->getDB($username);
|
||||||
if($user==false) {
|
if($user==false) {
|
||||||
Log::set(__METHOD__.LOG_SEP.'Username does not exist: '.$username);
|
Log::set(__METHOD__.LOG_SEP.'Username does not exist: '.$username);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$passwordHash = sha1($password.$user['salt']);
|
$passwordHash = $this->dbUsers->generatePasswordHash($password, $user['salt']);
|
||||||
|
if ($passwordHash===$user['password']) {
|
||||||
if($passwordHash === $user['password'])
|
|
||||||
{
|
|
||||||
$this->setLogin($username, $user['role']);
|
$this->setLogin($username, $user['role']);
|
||||||
|
|
||||||
Log::set(__METHOD__.LOG_SEP.'User logged succeeded by username and password - Username: '.$username);
|
Log::set(__METHOD__.LOG_SEP.'User logged succeeded by username and password - Username: '.$username);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -130,27 +128,14 @@ class Login {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function fingerPrint($random=false)
|
public function fingerPrint()
|
||||||
{
|
{
|
||||||
// User agent
|
// User agent
|
||||||
$agent = getenv('HTTP_USER_AGENT');
|
$agent = getenv('HTTP_USER_AGENT');
|
||||||
if (empty($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);
|
return sha1($agent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
<?php defined('BLUDIT') or die('Bludit CMS.');
|
<?php defined('BLUDIT') or die('Bludit Badass CMS.');
|
||||||
|
|
||||||
class Security extends dbJSON
|
class Security extends dbJSON
|
||||||
{
|
{
|
||||||
private $dbFields = array(
|
private $dbFields = array(
|
||||||
'key1'=>'Where we go we dont need roads',
|
|
||||||
'minutesBlocked'=>5,
|
'minutesBlocked'=>5,
|
||||||
'numberFailuresAllowed'=>10,
|
'numberFailuresAllowed'=>10,
|
||||||
'blackList'=>array()
|
'blackList'=>array()
|
||||||
|
@ -11,51 +10,33 @@ class Security extends dbJSON
|
||||||
|
|
||||||
function __construct()
|
function __construct()
|
||||||
{
|
{
|
||||||
parent::__construct(PATH_DATABASES.'security.php');
|
parent::__construct(DB_SECURITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authentication key
|
|
||||||
// --------------------------------------------------------------------
|
|
||||||
public function key1()
|
|
||||||
{
|
|
||||||
return $this->db['key1'];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// TOKEN FOR CSRF
|
// TOKEN FOR CSRF
|
||||||
// ====================================================
|
// ====================================================
|
||||||
|
|
||||||
// Generate and save the token in Session.
|
// Generate and save the token in Session
|
||||||
public function generateTokenCSRF()
|
public function generateTokenCSRF()
|
||||||
{
|
{
|
||||||
$token = Text::randomText(8);
|
$token = sha1( uniqid().time() );
|
||||||
$token = sha1($token);
|
|
||||||
|
|
||||||
Log::set(__METHOD__.LOG_SEP.'New tokenCSRF was generated '.$token);
|
|
||||||
|
|
||||||
Session::set('tokenCSRF', $token);
|
Session::set('tokenCSRF', $token);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate the token.
|
// Validate the token
|
||||||
public function validateTokenCSRF($token)
|
public function validateTokenCSRF($token)
|
||||||
{
|
{
|
||||||
$sessionToken = Session::get('tokenCSRF');
|
$sessionToken = $this->getTokenCSRF();
|
||||||
|
|
||||||
return ( !empty($sessionToken) && ($sessionToken===$token) );
|
return ( !empty($sessionToken) && ($sessionToken===$token) );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the token.
|
// Returns the token
|
||||||
public function getTokenCSRF()
|
public function getTokenCSRF()
|
||||||
{
|
{
|
||||||
return Session::get('tokenCSRF');
|
return Session::get('tokenCSRF');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function printTokenCSRF()
|
|
||||||
{
|
|
||||||
echo Session::get('tokenCSRF');
|
|
||||||
}
|
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// BRUTE FORCE PROTECTION
|
// BRUTE FORCE PROTECTION
|
||||||
// ====================================================
|
// ====================================================
|
||||||
|
@ -73,51 +54,42 @@ class Security extends dbJSON
|
||||||
$numberFailures = $userBlack['numberFailures'];
|
$numberFailures = $userBlack['numberFailures'];
|
||||||
$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']*60)) {
|
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);
|
Log::set(__METHOD__.LOG_SEP.'IP Blocked:'.$ip);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise the IP is not blocked.
|
// Otherwise the IP is not blocked
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addLoginFail()
|
// Add or update the current client IP on the blacklist
|
||||||
|
public function addToBlacklist()
|
||||||
{
|
{
|
||||||
$ip = $this->getUserIp();
|
$ip = $this->getUserIp();
|
||||||
$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];
|
$userBlack = $this->db['blackList'][$ip];
|
||||||
$lastFailure = $userBlack['lastFailure'];
|
$lastFailure = $userBlack['lastFailure'];
|
||||||
|
|
||||||
// Check if the IP is expired, then renew the number of failures.
|
// Check if the IP is expired, then renew the number of failures
|
||||||
if($currentTime <= $lastFailure + ($this->db['minutesBlocked']*60))
|
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);
|
Log::set(__METHOD__.LOG_SEP.'Blacklist, IP:'.$ip.', Number of failures:'.$numberFailures);
|
||||||
|
return $this->save();
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getNumberFailures($ip=null)
|
public function getNumberFailures($ip=null)
|
||||||
|
@ -134,14 +106,13 @@ class Security extends dbJSON
|
||||||
|
|
||||||
public function getUserIp()
|
public function getUserIp()
|
||||||
{
|
{
|
||||||
// User IP
|
if (getenv('HTTP_X_FORWARDED_FOR')) {
|
||||||
if(getenv('HTTP_X_FORWARDED_FOR'))
|
|
||||||
$ip = 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');
|
$ip = getenv('HTTP_CLIENT_IP');
|
||||||
else
|
} else {
|
||||||
$ip = getenv('REMOTE_ADDR');
|
$ip = getenv('REMOTE_ADDR');
|
||||||
|
}
|
||||||
return $ip;
|
return $ip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
|
@ -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_put_contents(PATH_DATABASES.'syslog.php', $dataHead.json_encode($data, JSON_PRETTY_PRINT), LOCK_EX);
|
||||||
|
|
||||||
// File security.php
|
// File security.php
|
||||||
$randomKey = sha1( uniqid() );
|
|
||||||
|
|
||||||
$data = array(
|
$data = array(
|
||||||
'key1'=>$randomKey,
|
|
||||||
'minutesBlocked'=>5,
|
'minutesBlocked'=>5,
|
||||||
'numberFailuresAllowed'=>10,
|
'numberFailuresAllowed'=>10,
|
||||||
'blackList'=>array()
|
'blackList'=>array()
|
||||||
|
|
Loading…
Reference in New Issue