PDA

View Full Version : Mys 1.3.3 Anti Brute-Force Attack Mod


pachoofoosh
08-06-2013, 03:55 PM
A simple way to help stop brute-force attacks, taking advantage of PHP's sleep() function.

This mod automatically records login attempts to the database. If an IP has 5 or more login attempts, the script will start delaying for them, slowing the attack to a crawl. On successful login, all of the IP's login attempts are cleared.

How to 'install'


Step 1.) Open up classes/class_sidebar.php. Replace its contents with the following:
<?php

/**
* The Sidebar Class, defines a standard HTML Sidebar component.
* It extends from the Widget class, while adding its own implementation.
* @category Resource
* @package Widget
* @author Hall of Famer
* @copyright Mysidia Adoptables Script
* @link http://www.mysidiaadoptables.com
* @since 1.3.3
* @todo Not much at this point.
*
*/

class Sidebar extends Widget{

/**
* The moneyBar property, specifies the money/donation bar for members.
* @access protected
* @var Paragraph
*/
protected $moneyBar;

/**
* The linksBar property, stores all useful links for members.
* @access protected
* @var Paragraph
*/
protected $linksBar;

/**
* The wolBar property, determines the who's online url in the sidebar.
* @access protected
* @var Link
*/
protected $wolBar;

/**
* The loginBar property, specifies the loginBar for guests.
* @access protected
* @var FormBuilder
*/
protected $loginBar;

/**
* Constructor of Sidebar Class, it initializes basic sidebar properties
* @access public
* @return Void
*/
public function __construct(){
$mysidia = Registry::get("mysidia");
$userLevel = ($mysidia->user->isloggedin)?"member":"visitor";
$stmt = $mysidia->db->select("modules", array(), "widget = 'sidebar' and (userlevel = '{$userLevel}' or userlevel = 'user') and status = 'enabled' ORDER BY `order`");

while($module = $stmt->fetchObject()){
$method = "set{$module->name}";
if($this->hasMethod($method)) $this->$method();
else $this->setModule($module);
}
}

/**
* The setDivision method, setter method for property $division.
* It is set internally upon object instantiation, cannot be accessed in client code.
* @param GUIComponent $module
* @access protected
* @return Void
*/
protected function setDivision(GUIComponent $module){
if(!$this->division){
$this->division = new Division;
$this->division->setClass("sidebar");
}
$this->division->add($module);
}

/**
* The getMoneyBar method, getter method for property $moneyBar.
* @access public
* @return Paragraph
*/
public function getMoneyBar(){
return $this->moneyBar;
}

/**
* The setMoneyBar method, setter method for property $moneyBar.
* It is set internally upon object instantiation, cannot be accessed in client code.
* @access protected
* @return Void
*/
protected function setMoneyBar(){
$mysidia = Registry::get("mysidia");
$this->moneyBar = new Paragraph;
$this->moneyBar->add(new Comment("You have {$mysidia->user->money} {$mysidia->user->cost}."));

$donate = new Link("donate");
$donate->setText("Donate Money to Friends");
$this->moneyBar->add($donate);
$this->setDivision($this->moneyBar);
}

/**
* The getLinksBar method, getter method for property $linksBar.
* @access public
* @return Paragraph
*/
public function getLinksBar(){
return $this->linksBar;
}

/**
* The setLinksBar method, setter method for property $linksBar.
* It is set internally upon object instantiation, cannot be accessed in client code.
* @access protected
* @return Void
*/
protected function setLinksBar(){
$mysidia = Registry::get("mysidia");
$this->linksBar = new Paragraph;
$linkTitle = new Comment("My Links:");
$linkTitle->setBold();
$this->linksBar->add($linkTitle);

$linksList = new LinksList("ul");
$this->setLinks($linksList);

$this->linksBar->add($linksList);
$this->setDivision($this->linksBar);
}

/**
* The setLinks method, append all links to the LinksBar.
* It is set internally upon object instantiation, cannot be accessed in client code.
* @access protected
* @return Void
*/
protected function setLinks(LinksList $linksList){
$mysidia = Registry::get("mysidia");
$stmt = $mysidia->db->select("links", array("id", "linktext", "linkurl"), "linktype = 'sidelink' ORDER BY linkorder");
if($stmt->rowCount() == 0) Throw new Exception("There is an error with sidebar links, please contact the admin immediately for help.");

while($sideLink = $stmt->fetchObject()){
$link = new Link($sideLink->linkurl);
$link->setText($sideLink->linktext);
if($sideLink->linkurl == "messages"){
$num = $mysidia->db->select("messages", array("touser"), "touser='{$mysidia->user->username}' and status='unread'")->rowCount();
if($num > 0) $link->setText("{$link->getText()} ({$num})");
}
$link->setListed(TRUE);
$linksList->add($link);
}

if($mysidia->user instanceof Admin){
$adminCP = new Link("admincp/", FALSE, FALSE);
$adminCP->setText("Admin Control Panel");
$adminCP->setListed(TRUE);
$linksList->add($adminCP);
}
}

/**
* The getWolBar method, getter method for property $wolBar.
* @access public
* @return LinksList
*/
public function getWolBar(){
return $this->wolBar;
}

/**
* The setWolBar method, setter method for property $wolBar.
* It is set internally upon object instantiation, cannot be accessed in client code.
* @access protected
* @return Void
*/
protected function setWolBar(){
$mysidia = Registry::get("mysidia");
$this->wolBar = new Link("online");
$online = $mysidia->db->select("online", array(), "username != 'Visitor'")->rowCount();
$offline = $mysidia->db->select("online", array(), "username = 'Visitor'")->rowCount();
$this->wolBar->setText("There are {$online} users and {$offline} guests online.");
$this->setDivision($this->wolBar);

}

/**
* The getLoginBar method, getter method for property $loginBar.
* @access public
* @return FormBuilder
*/
public function getLoginBar(){
return $this->loginBar;
}

/**
* The setLoginBar method, setter method for property $loginBar.
* It is set internally upon object instantiation, cannot be accessed in client code.
* @access protected
* @return Void
*/
protected function setLoginBar(){

include ('/functions/functions.php');

$this->loginBar = new FormBuilder("login", "login", "post");
$loginTitle = new Comment("Member Login:");
$loginTitle->setBold();
$loginTitle->setUnderlined();
$this->loginBar->add($loginTitle);

$this->loginBar->buildComment("<label for='username'>username:</label><br /> ", FALSE)
->buildTextField("username")
->buildComment("<label for='password'>password:</label><br /> ", FALSE)
->buildPasswordField("password", "password", "", TRUE)
->buildButton("Log In", "submit", "submit")
->buildComment("Don't have an account?");

$register = new Link("register");
$register->setText("Register new Account");
$register->setLineBreak(TRUE);
$forgot = new Link("forgotpass");
$forgot->setText("Forgot password?");

$this->loginBar->add($register);
$this->loginBar->add($forgot);
$this->setDivision($this->loginBar);
}

public function setModule($module){
$mysidia = Registry::get("mysidia");
$moduleContainer = new Paragraph();
if($module->subtitle) $moduleContainer->add(new Comment($module->subtitle, TRUE, "b"));
if($module->php) eval($module->php);
if($module->html) $moduleContainer->add(new Comment($module->html));
$this->setDivision($moduleContainer);
}

/**
* The render method for Sidebar class, it renders the division component and thus all subcomponents.
* @access public
* @return String
*/
public function render(){
return $this->division->render();
}
}
?>

Step 2.) Open up functions/functions.php. Now let's create the anti brute-force function, logAttempt(). Paste the following into the bottom of the file, just above the ending PHP tag (?>):
function logAttempt() {

$mysidia = Registry::get("mysidia");
$ip = $_SERVER['REMOTE_ADDR'];
$query = $mysidia->db->select("login_attempts", array("num_attempts"), "ip = '$ip'")->rowCount();
$attempts = $query + 1;

$stmt = $mysidia->db->insert("login_attempts", array("num_attempts" => "$attempts", "ip" => "$ip"));
if( $attempts <= 4 ) { // Equal to or less than 4 login attempts..
$delay_time = 0;
}
elseif( $attempts == 5 ) { // Five login attempts...
$delay_time = 2;
}
elseif( $attempts >= 6 ) { // Greater than or equal to 6 login attempts...
$delay_time = 5;
}
return sleep($delay_time);
} // End logAttempt function.

Step 3.) We're almost finished; halfway there. c; Open up login.php. Replace its contents with the following:
<?php

class LoginController extends AppController{

private $view;
private $subController;

public function __construct(){
parent::__construct();
}

public function index(){
$this->flag = "guest";
$this->handleAccess();
$mysidia = Registry::get("mysidia");
$document = $mysidia->frame->getDocument();

if($mysidia->input->post("submit")){
if(!$mysidia->input->post("username") or !$mysidia->input->post("password")){
$document->setTitle($mysidia->lang->fail_title);
$document->addLangvar($mysidia->lang->fail_blank);
}
else{
$validator = new UserValidator($mysidia->user, array("username" => $mysidia->input->post("username"), "password" => $mysidia->input->post("password")));
$validator->validate("username");
$validator->validate("password");

if(!$validator->triggererror()){
// Validation succeeded, now begins the actual login procedure

$document->setTitle($mysidia->lang->success_title);
$document->addLangvar("Welcome back, {$mysidia->input->post("username")}! {$mysidia->lang->success}");
$ip = $_SERVER['REMOTE_ADDR']; // Delete the user's previous login attempts.
$mysidia->db->delete("login_attempts", "ip = '$ip'");

$mysidia->user->login($mysidia->input->post("username"));
if($mybbenabled == 1) $mysidia->user->loginforum();
$mysidia->session->terminate("clientip");
}
else{

$document->setTitle($mysidia->lang->fail_title);
$document->addLangvar($mysidia->lang->fail_details);
logAttempt();
$mysidia->cookies->logincookies();


}
}
return;
}

$mysidia->session->assign("clientip", $_SERVER['REMOTE_ADDR']);
$document->setTitle($mysidia->lang->title);
$document->addLangvar($mysidia->lang->login);

$loginForm = $mysidia->frame->getSidebar()->getLoginBar();
$document->add($loginForm);
}

public function logout(){
$this->flag = "member";
$this->handleAccess();
$mysidia = Registry::get("mysidia");
$document = $mysidia->frame->getDocument();

$mysidia->user->logout();
$document->setTitle($mysidia->lang->logout_title);
$document->addLangvar($mysidia->lang->logout);
}
}
?>

Step 4.) And finally, let's create the login_attempts table. uwu Open up PHPMyAdmin and click on the database you're going to use. Next, click the button that says '(+) New table' (At the bottom of the frame on the left side of the screen which displays all of the DB's tables).

For the criteria, it should look something like this:
http://i42.tinypic.com/8wh4qo.png

And with that, everything is finished! :D If you come across any errors while using this Mod, I'll be happy to help fix it. Hope you guys will find good use of it! c:

Hall of Famer
08-06-2013, 04:05 PM
Looks interesting, hopefully it will help people out. ^^

Nemesis
08-08-2013, 12:55 PM
Its a great mod no doubt. I am just wondering if it has ever been a problem though.

Kesstryl
08-08-2013, 04:43 PM
Whether it has ever been a problem or not, I don't think there can ever be enough security measures. php is so easy to crack and hack. Thanks for this mod!

pachoofoosh
08-09-2013, 10:10 AM
Hall of Famer: Thanks! I hope so as well, you can never be safe enough with php. ^^

Nemesis: Thanks! c: I'm not really sure. cx Haha I tend to be rather paranoid when it comes to php, especially with anything that involves user input. Either way, brute-force attacks are vital to have prevention mechanisms for.

Kesstryl: Thank you! That is very true. uwu Hope you find this mod useful! :usedusedused: