Hey, just made the title like that, to grab intrest ;)

I'm developing a system at the moment, i'm going to implement a filter to the login section, so that only 5 invalid login attempts can be made, then its temp denied access to login with that username, for 15 minutes.
Making it virtually pointless to try and brute force the login.

Just curious if anyone has any existing methods of doing this, or any coding suggestions/tips that they can throw my way, before i start implementing it.

Cheers.
Paul.

Recommended Answers

All 16 Replies

In my opinion, you should create $_SESSION and make it
expire every 5 minutes. Everytimes user fail to login, $_SESSION[attempt]
will increase by 1 if user attempt more than 5 times then disable login
page. This is what I think it should be.

Sorry mabye i was a bit unclear - if the user login fails 5 times WITHIN 15 mins, it will block it for 15 minutes.

Intresting concept invisal . any more ?

Everytime user fail to login, set session expire to next 15 minutes. so mean that if they fail more than 5 times it will be block 15 minutes

Maybe it is little unclear. So I give you a real time example:

First I fail to login at : 0:00am so the SESSION will be expire in 0:15am
However, I attempt again but fail at 0:05am so the SESSION will be expire in 0:20am and that $_SESSION probally will be = 2.
I try again at 0:15am and SESSION is not expire yet. This time I fail again. So my session expire will be last until 0:30am

I like the way your thinking Invisal.

I will wait for more replies from other coders, before i decide which is the more suitable method to use.

Thanks for your input ;)

Maybe it is little unclear. So I give you a real time example:

First I fail to login at : 0:00am so the SESSION will be expire in 0:15am
However, I attempt again but fail at 0:05am so the SESSION will be expire in 0:20am and that $_SESSION probally will be = 2.
I try again at 0:15am and SESSION is not expire yet. This time I fail again. So my session expire will be last until 0:30am

I'm developing a system at the moment, i'm going to implement a filter to the login section, so that only 5 invalid login attempts can be made, then its temp denied access to login with that username, for 15 minutes.

A better way to implement this would be to forget about the user trying the brute force all together, but try and detect a brute force attempt by a pattern in failed login attempts.

You can start simply by saving each failed login attempts to a database.
A simple pattern is 5 failed login attempts on a username. This is without regards to who made the attempts or from where or what IP (these are factors that can be changed by the attacker), just the fact that there exists 5 failed login attempts on a single username in the last 15 minutes.

Of course, you could also try the IP, for those users that don't use a distributed brute force attack, but just use a single IP range. If you have 5 failed login attempts from a single IP, or similar range, no matter what username it is, they it may be a good basis for seeing it as a brute force.

The reason for this is because most brute force attacks would span from different computers that have no common properties as far as your php application can gather. $_SESSION is useless here as it is implemented via HTTP Cookies (or HTTP GET url parameters).

One want to slow down a brute force attack would be to make sure a brute force attempt is not viable. Brute force works on the ability to process multiple attempts on the system very quickly, either from a single computer, or from many. If you place a simple:

<?php sleep(10); ?>

it makes brute force less viable, especially for one that has a low probability of finding a username/password match. This is good for attacks which are hard to track like distributed computers sending login attempts on different usernames all at once. They would all have to wait 10 seconds before knowing if the result. For a computer attempting a brute force, thats a century. For the user, it may actually seem more secure - especially if you have a huge sign, "Authenticating...". :)

If I were to take a guess I'd think Paypal, Ebay etc. use this technique. You have to wait around 1 minute for your login. Now that isn't because it takes that long to authenticate you (maybe it does an thats a plus), and it doesn't matter how fast your connection is, it still takes 1 minute.

To prevent from those attacker that use robot to do multi-attempt, I think
we can use Secure Code Image technique. Robot cannot guess what
code that contain in the image so that robot is useless to attack us.

To prevent from those attacker that use robot to do multi-attempt, I think
we can use Secure Code Image technique. Robot cannot guess what
code that contain in the image so that robot is useless to attack us.

Yes, a CAPCHA is a great method, I'd like to note a few things that usually aren't considered with CAPTCHAs on login forms, as opposed to on on a non-authenticated form. (such as a public comment form etc.)

A CAPTCHA is ok for preventing spam, but not so much for brute force. You can use it to stop the bulk of attakers, but it does not stop the determined brute force attack.

The CAPTCHA is subject to a few things that a simple thing such as <?php sleep(10); ?> isn't.

1) Can be read by OCR (Optical Character Recognition)
2) Subject to Social based attacks (using people to attack without their knowledge)
3) Subject to Session based attacks

A good number of generated CAPTCHAs can be read by a bot that uses Optical Character Recognition (OCR). At times a bot can be faster than a human at recognizing an optical character.
Even if a bot can only recognize 1% of your generated CAPTCHAs, it has the ability to launch a brute force attack.
The reason CAPTCHAs are used is that they slow down bots. They require a lot of processing power to run numerous OCR software on an image, and only get 1% favorable outcome to exploit on your sever (thus it costs alot).
If the incentive was to post spam on your blog, then a bot would not be interested in wasting so much money on it. If the incentive was to figure out a users password, such as in a brute force, then matching a CAPTCHAs 1% of the time is a doable expense.

Attackers can also use social based attacks on CAPTCHAs. A simple example is generating 1000s of sessions on your server which generate 1000s of CAPTCHA images. The images that cannot be solved by OCR are then placed on login forms, comment forms, forum post forms, etc. on other websites. Users using these websites do not realize they are contributing to a brute force attack. On high traffic websites, the attacker can launch an attack in seconds once they have collected enough validated CAPTCHAs.

Since a CAPTCHA is session based, the single attack can be postponed untill the attacker has 1000s of user or OCR validated CAPTCHAs (before the session times out). Thus the longer they carry out the attack, the faster they can make authentication attempts as they collect more and more user validated CAPTCHAs and store it for the next attempts.

It is not 100% match for using OCR to read the image right? Plus the image is created which randomly content. Even the attacker collect more than 1000 of images that have been appear on the login, the next image will be probally different from the previous. I guess the robots aren't so effective after all now. Correct me if i am wrong...

It is not 100% match for using OCR to read the image right? Plus the image is created which randomly content. Even the attacker collect more than 1000 of images that have been appear on the login, the next image will be probally different from the previous. I guess the robots aren't so effective after all now. Correct me if i am wrong...

The login form is just there for the browser. It is only a "template" to tell the browser how to make the next HTTP Request to the server.
A bot will not need the login form, just the correct details to make the valid HTTP Request.. It only needs to get the CAPTCHA image, see if it can guess it or not. If it thinks OCR has a match, it will then make a login attempt.
So, it is still affective if it downloads many images. If the server can serve 50 000 images a second, then the bot can make 500 login attempts a second based on the OCR being able to guess 1% of those.

commented: helpful input +1

Thanks for your input digital-ether.
CAPTCHA is already going to be implemented to minimalise the risk of automated attacks.

The database idea, i'll go with. However you mentioned;

A simple pattern is 5 failed login attempts on a username. This is without regards to who made the attempts or from where or what IP

You didn't talk about what to do if theres 5 failed login attempts, were you agreeing with that fact that disabling user login for 15 minutes?

Cheers.

Hiya,
I'm new here :) I've arrived here with a google train, in search of a method to limit or slow down brute force attemps.

I agree with many of you, but I also think that we can only slow down an attacker. Best thing we can do is logging the attemps.

A simple pattern is 5 failed login attempts on a username. This is without regards to who made the attempts or from where or what IP (these are factors that can be changed by the attacker), just the fact that there exists 5 failed login attempts on a single username in the last 15 minutes.

I agree with you, and probably this is the best solution, but an attacker could always write a brute force program that tries four password per username and then switch to the next username until the timeout is finished, no?

Or my guess is just fantasy?! :D

...I agree with you, and probably this is the best solution, but an attacker could always write a brute force program that tries four password per username and then switch to the next username until the timeout is finished, no?

Or my guess is just fantasy?! :D

Yes, an attacker could always do that. The smarter our code that looks for patterns the better. 5 attempts on a username is just an example.

A brute force works on two things, speed and probability. If you can slow down the attack, and reduce the success probability, you stop the brute force.

If a brute force can attempt a username with different passwords only 4 times, then the probability is low that it will be a success. Even if you have 10000 users, thats only 40000 attempts (assuming your user list is available to the attacker), which is has a very low probability compared to an infinite number of attempts.

Yes, indeed. Thanks for the reply :)

Just posting to thank digital-ether for the posts. Love the social based CAPTCHA example.

Spend some time researching because I want to implement this feature as well. This is what I came up with. Comments appreciated.

DB table:
CREATE TABLE `auth` (
`id` int(11) NOT NULL,
`username` varchar(40) NOT NULL,
`password` varchar(60) NOT NULL,
`email` varchar(60) NOT NULL,
`role` varchar(10) NOT NULL default '0',
`timeStamp` varchar(20) NOT NULL,
`incorrect` tinyint(4) NOT NULL default '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

used varchar for timeStamp so I could store unix timestamp (MySQL did not like format). DateTime has too many variables to account for (i.e. what if the time is 23:50?) and is a bit of a pain to convert.
Now the Goods:

function protectLogin(){
	  Global $dbx;
	  $protect=false;
	  $user=$_POST['username'];
	  $password=$_POST['password'];
	  $query= "Select * from auth where username = '".mysql_real_escape_string($user)."'";    
      $userQuery = $dbx->query($query);		
      if($row = $userQuery->fetchRow(DB_FETCHMODE_ASSOC)){
       
			 $_SESSION['authorize']['id']=$row['id'];
			 $_SESSION['authorize']['username']=$row['username'];
			 $_SESSION['authorize']['timeStamp']=$row['timeStamp'];
			 $_SESSION['authorize']['incorrect']=$row['incorrect'];			
			 $_SESSION['authorize']['role']=$row['role'];			 
			 
			 if( md5($password)==$row['password'] && ($row['incorrect']!=5) ){
			  	//echo " passwords match </br>";//set session
			 	$_SESSION['authorize']['register']=1; 				 
				header ("Location: ./index.php");										
			 }else{			
			   $protect=true;//incorrect password or has had 5 incorrect logins
			 }					        
	  }		
	  $userQuery->free();		
	  return $protect;
}

//***********************************************************************************
// this section displays email form for lost passwords shown only when login fails		
//***********************************************************************************
function wrongPass($tpl){
    Global $dbx; 
	//add variables from db
	$id=$_SESSION['authorize']['id'];
	$user=$_SESSION['authorize']['username'];	 
	$date=$_SESSION['authorize']['timeStamp'];
	$incorrect=$_SESSION['authorize']['incorrect'];	
	// $ip=$_SERVER['REMOTE_ADDR'];//if you want to save the ip of incorrect logins	 						
		
		//echo " the unix timestamp stored is <br>".$date."<BR>";
		//echo " the date is ".date("F j, Y, g:i a",$date)."<BR>";
		$storedDate=$date+900;//if less than this increment count
		//echo " date plus 900 seconds ".date("F j, Y, g:i a",$storedDate)."<BR>";
		
		$time=mktime();//current time
		//echo " the unix timestamp now is <br>".$time."<BR>";		
		//echo " incorrect attempts = $incorrect <br>";
		//echo " the date is ".date("F j, Y, g:i a",$time)."<BR>";
				
		$query="UPDATE `auth` SET ";
		if($storedDate>$time){//within 15 minutes of previous attempt		   
		   if($incorrect!=5){//not already 5 increase number
		     $query.=" `incorrect`='".($incorrect+1)."'";
		     $query.="WHERE `id`='".$id."'";
		     //echo $query."<BR>";
	 	     $dbx->query($query);
		   }		
		}else{//first invalid attempt in fifteen minutes		   	
			$query.="`timeStamp`= '".$time."', `incorrect`='1'";
		    $query.="WHERE `id`='".$id."'";
		    //echo $query."<BR>";
	 	    $dbx->query($query);
		}		
	
	    $lost=centerColumnHead("Unable to login!!!", "Please Try again Later");			
		$lost2="<br /><br /></div> </td></tr>";		
	
		$tpl->setVariable("centerColumnContent", $lost);
		$tpl->setVariable("centerColumnContent2", $lost2);
		
	return $tpl;	
}

What could you do with the ip address of the incorrect logins?

Just posting to thank digital-ether for the posts. Love the social based CAPTCHA example.

Spend some time researching because I want to implement this feature as well. This is what I came up with. Comments appreciated.

DB table:
CREATE TABLE `auth` (
`id` int(11) NOT NULL,
`username` varchar(40) NOT NULL,
`password` varchar(60) NOT NULL,
`email` varchar(60) NOT NULL,
`role` varchar(10) NOT NULL default '0',
`timeStamp` varchar(20) NOT NULL,
`incorrect` tinyint(4) NOT NULL default '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

used varchar for timeStamp so I could store unix timestamp (MySQL did not like format). DateTime has too many variables to account for (i.e. what if the time is 23:50?) and is a bit of a pain to convert.
Now the Goods:

function protectLogin(){
	  Global $dbx;
	  $protect=false;
	  $user=$_POST['username'];
	  $password=$_POST['password'];
	  $query= "Select * from auth where username = '".mysql_real_escape_string($user)."'";    
      $userQuery = $dbx->query($query);		
      if($row = $userQuery->fetchRow(DB_FETCHMODE_ASSOC)){
       
			 $_SESSION['authorize']['id']=$row['id'];
			 $_SESSION['authorize']['username']=$row['username'];
			 $_SESSION['authorize']['timeStamp']=$row['timeStamp'];
			 $_SESSION['authorize']['incorrect']=$row['incorrect'];			
			 $_SESSION['authorize']['role']=$row['role'];			 
			 
			 if( md5($password)==$row['password'] && ($row['incorrect']!=5) ){
			  	//echo " passwords match </br>";//set session
			 	$_SESSION['authorize']['register']=1; 				 
				header ("Location: ./index.php");										
			 }else{			
			   $protect=true;//incorrect password or has had 5 incorrect logins
			 }					        
	  }		
	  $userQuery->free();		
	  return $protect;
}

//***********************************************************************************
// this section displays email form for lost passwords shown only when login fails		
//***********************************************************************************
function wrongPass($tpl){
    Global $dbx; 
	//add variables from db
	$id=$_SESSION['authorize']['id'];
	$user=$_SESSION['authorize']['username'];	 
	$date=$_SESSION['authorize']['timeStamp'];
	$incorrect=$_SESSION['authorize']['incorrect'];	
	// $ip=$_SERVER['REMOTE_ADDR'];//if you want to save the ip of incorrect logins	 						
		
		//echo " the unix timestamp stored is <br>".$date."<BR>";
		//echo " the date is ".date("F j, Y, g:i a",$date)."<BR>";
		$storedDate=$date+900;//if less than this increment count
		//echo " date plus 900 seconds ".date("F j, Y, g:i a",$storedDate)."<BR>";
		
		$time=mktime();//current time
		//echo " the unix timestamp now is <br>".$time."<BR>";		
		//echo " incorrect attempts = $incorrect <br>";
		//echo " the date is ".date("F j, Y, g:i a",$time)."<BR>";
				
		$query="UPDATE `auth` SET ";
		if($storedDate>$time){//within 15 minutes of previous attempt		   
		   if($incorrect!=5){//not already 5 increase number
		     $query.=" `incorrect`='".($incorrect+1)."'";
		     $query.="WHERE `id`='".$id."'";
		     //echo $query."<BR>";
	 	     $dbx->query($query);
		   }		
		}else{//first invalid attempt in fifteen minutes		   	
			$query.="`timeStamp`= '".$time."', `incorrect`='1'";
		    $query.="WHERE `id`='".$id."'";
		    //echo $query."<BR>";
	 	    $dbx->query($query);
		}		
	
	    $lost=centerColumnHead("Unable to login!!!", "Please Try again Later");			
		$lost2="<br /><br /></div> </td></tr>";		
	
		$tpl->setVariable("centerColumnContent", $lost);
		$tpl->setVariable("centerColumnContent2", $lost2);
		
	return $tpl;	
}

What could you do with the ip address of the incorrect logins?

The code looks good to me.

If you'll need an extra table to log IPs of unsuccessful attempts, you might as well have a db table that logs every single unsuccessful login attempt within a certain time period to the present. That way you can use that table to determine the number of failures on a username, as well as from a single IP, or other data etc..

Eg:

CREATE TABLE `failed_auth` (
`id` int(11) NOT NULL,
`username` varchar(40) NOT NULL,
`password` varchar(60) NOT NULL,
`ip` varchar(8) NOT NULL,
`time` timestamp NOT NULL default CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
)

Eg Queries:

"SELECT count(id) FROM failed_auth WHERE username = '$username' AND time > (NOW() - 3600)"; // failed attempts in last hour for username
"SELECT count(ip) FROM failed_auth WHERE ip = '$ip' AND time > (NOW() - 3600)"; // select failed attemps from a single IP in last hour

This would also allow you to retrieve other simple patterns that tell you of a brute force. Eg. Your failed authentications table is getting larger than your users table...

"SELECT (count(id) > (SELECT count(id) FROM users) FROM failed_auth WHERE f.time > (NOW() - 3600)";
; // every single user made a failed attempt in the last hour? don't think so

This can determine when you want to show a CAPTCHA with your login form, or start delaying authentications for a few secs.

A less obvious pattern would be a dictionary based attack. If you see username/password start incrementing in a dictionary order, you could tell its a dictionary attack.

The table shouldn't get that big. It should stay smaller than your user's table if old rows are deleted when they expire..

I've never implemented anything like this on a live site. Its just an idea. Maybe its overkill?

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.