added login rate limit
This commit is contained in:
87
classes/LoginFail.php
Normal file
87
classes/LoginFail.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 2010-2022 Webkul.
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* All right is reserved,
|
||||
* Please go through this link for complete license : https://store.webkul.com/license.html
|
||||
*
|
||||
* DISCLAIMER
|
||||
*
|
||||
* Do not edit or add to this file if you wish to upgrade this module to newer
|
||||
* versions in the future. If you wish to customize this module for your
|
||||
* needs please refer to https://store.webkul.com/customisation-guidelines/ for more information.
|
||||
*
|
||||
* @author Webkul IN <support@webkul.com>
|
||||
* @copyright 2010-2022 Webkul IN
|
||||
* @license https://store.webkul.com/license.html
|
||||
*/
|
||||
|
||||
class LoginFailCore extends ObjectModel
|
||||
{
|
||||
|
||||
public $id_failed_login;
|
||||
public $ip_address;
|
||||
public $email;
|
||||
public $created_at;
|
||||
const USERNAME_ATTEMPTS_PER_QUARTER_HOUR = 3;
|
||||
const IP_ATTEMPTS_QUARTER_HOUR = 12;
|
||||
const USERNAME_ATTEMPTS_PER_HOUR = 6;
|
||||
const IP_ATTEMPTS_PER_HOUR = 24;
|
||||
const QUARTER_HOUR = 60*15;
|
||||
const HOUR = 60*60;
|
||||
|
||||
/**
|
||||
* @see ObjectModel::$definition
|
||||
*/
|
||||
public static $definition = array(
|
||||
'table' => 'login_fail',
|
||||
'primary' => 'id_login_fail',
|
||||
'fields' => array(
|
||||
'ip_address' => array('type' => self::TYPE_STRING, 'size' => 50),
|
||||
'email' => array('type' => self::TYPE_STRING, 'validate' => 'isEmail', 'required' => true, 'size' => 128),
|
||||
),
|
||||
);
|
||||
|
||||
public function cleanData()
|
||||
{
|
||||
$time = (time() - (self::HOUR*2));
|
||||
Db::getInstance()->execute(
|
||||
'
|
||||
DELETE FROM `' . _DB_PREFIX_ . 'login_fail`
|
||||
WHERE `created_at` < "' . pSQL(date("Y-m-d H:i:s", $time)) . '"'
|
||||
);
|
||||
}
|
||||
|
||||
public function getUserFailedCount($time, $email=false, $ip_address=false, $attempts)
|
||||
{
|
||||
$sql = "SELECT COUNT(id_login_fail) FROM `". _DB_PREFIX_ ."login_fail` WHERE `created_at` > '"
|
||||
. pSQL(date('Y-m-d H:i:s', $time)) ."'";
|
||||
|
||||
if ($email) {
|
||||
$sql .= " AND `email` = '".pSQL($email)."'";
|
||||
}
|
||||
|
||||
if ($ip_address) {
|
||||
$sql .= " AND `ip_address` = '".pSQL($ip_address)."'";
|
||||
}
|
||||
return Db::getInstance()->getValue($sql) >= $attempts;
|
||||
}
|
||||
|
||||
public function checkLimit($email)
|
||||
{
|
||||
$has_error = false;
|
||||
$minutes_ago = time() - self::QUARTER_HOUR;
|
||||
$hours_ago = time() - self::HOUR;
|
||||
$ip_address = Tools::getRemoteAddr();
|
||||
|
||||
$has_error = $this->getUserFailedCount($minutes_ago, $email, false, self::USERNAME_ATTEMPTS_PER_QUARTER_HOUR)? true : $has_error;
|
||||
$has_error = $this->getUserFailedCount($minutes_ago, false, $ip_address, self::IP_ATTEMPTS_QUARTER_HOUR)? true : $has_error;
|
||||
$has_error = $this->getUserFailedCount($hours_ago, $email, false, self::USERNAME_ATTEMPTS_PER_HOUR)? true : $has_error;
|
||||
$has_error = $this->getUserFailedCount($hours_ago, false, $ip_address, self::IP_ATTEMPTS_PER_HOUR)? true : $has_error;
|
||||
|
||||
return $has_error;
|
||||
}
|
||||
}
|
||||
@@ -249,10 +249,7 @@ class FrontControllerCore extends Controller
|
||||
$this->context->smarty->assign(Meta::getMetaTags($this->context->language->id, $page_name));
|
||||
$this->context->smarty->assign('request_uri', Tools::safeOutput(urldecode($_SERVER['REQUEST_URI'])));
|
||||
|
||||
// Init cookie language
|
||||
// @TODO This method must be moved into switchLanguage
|
||||
Tools::setCookieLanguage($this->context->cookie);
|
||||
|
||||
$this->displayMaintenancePage();
|
||||
// If current URL use SSL, set it true (used a lot for module redirect)
|
||||
if (Tools::usingSecureMode()) {
|
||||
@@ -277,6 +274,9 @@ class FrontControllerCore extends Controller
|
||||
}
|
||||
ob_start();
|
||||
|
||||
// Init cookie language
|
||||
// @TODO This method must be moved into switchLanguage
|
||||
|
||||
$protocol_link = (Configuration::get('PS_SSL_ENABLED') || Tools::usingSecureMode()) ? 'https://' : 'http://';
|
||||
$useSSL = ((isset($this->ssl) && $this->ssl && Configuration::get('PS_SSL_ENABLED')) || Tools::usingSecureMode()) ? true : false;
|
||||
$protocol_content = ($useSSL) ? 'https://' : 'http://';
|
||||
@@ -750,7 +750,7 @@ class FrontControllerCore extends Controller
|
||||
*/
|
||||
protected function displayMaintenancePage()
|
||||
{
|
||||
if (isset(Context::getContext()->cookie->remote_addr)
|
||||
if (isset(Context::getContext()->cookie->remote_addr)
|
||||
&& Context::getContext()->cookie->remote_addr != ip2long(Tools::getRemoteAddr())
|
||||
) {
|
||||
$cookie = Context::getContext()->cookie;
|
||||
@@ -761,7 +761,7 @@ class FrontControllerCore extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
if (Tools::isSubmit('SubmitLogin')
|
||||
if (Tools::isSubmit('SubmitLogin')
|
||||
&& Configuration::get('PS_ALLOW_EMP')
|
||||
&& !Context::getContext()->cookie->enable_maintenance_view
|
||||
) {
|
||||
@@ -769,7 +769,7 @@ class FrontControllerCore extends Controller
|
||||
$passwd = trim(Tools::getValue('passwd'));
|
||||
$this->processLogin($email, $passwd);
|
||||
}
|
||||
|
||||
|
||||
if (!Context::getContext()->cookie->enable_maintenance_view || !Configuration::get('PS_ALLOW_EMP')) {
|
||||
Context::getContext()->cookie->__unset('enable_maintenance_view');
|
||||
Context::getContext()->cookie->write();
|
||||
@@ -777,7 +777,7 @@ class FrontControllerCore extends Controller
|
||||
$this->maintenance = true;
|
||||
if (!in_array(Tools::getRemoteAddr(), explode(',', Configuration::get('PS_MAINTENANCE_IP')))) {
|
||||
header('HTTP/1.1 503 temporarily overloaded');
|
||||
|
||||
|
||||
$this->context->smarty->assign($this->initLogoAndFavicon());
|
||||
$this->context->smarty->assign(array(
|
||||
'HOOK_MAINTENANCE' => Hook::exec('displayMaintenance', array()),
|
||||
@@ -796,6 +796,8 @@ class FrontControllerCore extends Controller
|
||||
protected function processLogin($email, $passwd)
|
||||
{
|
||||
$result = false;
|
||||
$objLoginFail = new LoginFail;
|
||||
|
||||
/* Check fields validity */
|
||||
if (empty($email)) {
|
||||
$this->errors[] = Tools::displayError('Email is empty.');
|
||||
@@ -809,6 +811,10 @@ class FrontControllerCore extends Controller
|
||||
$this->errors[] = Tools::displayError('Invalid password.');
|
||||
}
|
||||
|
||||
if ($objLoginFail->checkLimit($email)) {
|
||||
$this->errors[] =Tools::displayError('You have exceeded the limit of login attempts, please try after some time');
|
||||
}
|
||||
|
||||
if (!count($this->errors)) {
|
||||
// Find employee
|
||||
$this->context->employee = new Employee();
|
||||
@@ -829,6 +835,10 @@ class FrontControllerCore extends Controller
|
||||
}
|
||||
if (count($this->errors)) {
|
||||
$this->context->smarty->assign('errors', $this->errors);
|
||||
$objLoginFail->email = $email;
|
||||
$objLoginFail->ip_address = Tools::getRemoteAddr();
|
||||
$objLoginFail->save();
|
||||
$objLoginFail->cleanData();
|
||||
} else {
|
||||
Tools::redirect('index.php');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user