Page 1 of 1

Limiting login attempts Rate Topic: -----

#1 CTphpnwb  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 3077
  • View blog
  • Posts: 10,790
  • Joined: 08-August 08

Posted 25 August 2012 - 05:22 PM

This has come up many times in the forum so I decided to demonstrate a method using a database table and the ip address of the client.

The goal is to keep track of the number of times a login attempt is made from the same ip address within a defined number of seconds. In order to do that the first thing the function does is remove all previously stored attempts from the table that are too old to have happened within that number of seconds. This will keep the table from growing to the point where it might slow the server down and keep it from using too much space. The next step is to add this new attempt to the table, and finally, the script counts the number of attempts from this current ip address and returns the value. From there it's a simple matter for the script to decide whether or not to allow another login attempt based on the defined number of maximum attempts.

<?php
$dsn = "mysql:host=localhost;dbname=test";
$username = "root"; 
$password = "root";
// Above is my test database information. You'll need to use your own. See Dormilich's tutorial on PDO for more information.
$options = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION);

$pdo = new PDO($dsn, $username, $password, $options);
$max_time_in_seconds = 10;
$max_attempts = 3;

if(login_attempt_count($max_time_in_seconds, $pdo) <= $max_attempts) {
	echo "Please login<br>";
	// Now show the login form
} else {
	echo "I'm sorry, you've made too many attempts to log in too quickly.<br>";
	// Do not show the login form, since it may be a bot trying to log in.
}

function login_attempt_count($seconds, $pdo) {
	try {
		// First we delete old attempts from the table
		$del_old = "DELETE FROM attempts WHERE `when` < ?";
		$oldest = strtotime(date("Y-m-d H:i:s")." - ".$seconds." seconds");
		$oldest = date("Y-m-d H:i:s",$oldest);
		$del_data = array($oldest);
		$remove = $pdo->prepare($del_old);
		$remove->execute($del_data);
		
		// Next we insert this attempt into the table
		$insert = "INSERT INTO attempts (`ip`, `when`) VALUES ( ?, ? )";
		$data = array($_SERVER['REMOTE_ADDR'], date("Y-m-d H:i:s"));
		$input = $pdo->prepare($insert);
		$input->execute($data);
		
		// Finally we count the number of recent attempts from this ip address	
		$count = "SELECT count(*) as number FROM attempts where `ip` = ?";
		$num = $pdo->prepare($count);
		$num->execute(array($_SERVER['REMOTE_ADDR']));
		foreach($num as $attempt) {
			$attempts = $attempt['number'];
		}
		return $attempts;
	} catch (PDOEXCEPTION $e) {
		echo "Error: ".$e;
	}
}

The table:
CREATE TABLE `attempts` (
  `ip` varchar(20) NOT NULL,
  `when` datetime NOT NULL,
  KEY `ip` (`ip`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;



One more thing: I don't use a form in this demonstration, but you can test the script by reloading the page multiple times.

This post has been edited by CTphpnwb: 26 August 2012 - 09:48 AM


Is This A Good Question/Topic? 3
  • +

Replies To: Limiting login attempts

#2 RyanRobinson  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 39
  • View blog
  • Posts: 227
  • Joined: 31-March 10

Posted 26 August 2012 - 12:12 PM

Some good points here.

However, I'm not saying you're developing a website that's going to have a lot of traffic but, would querying the database be a resource hog for checking something simple. I haven't done this before but would it better to do this using Session data or is that more of an overhead?

This post has been edited by RyanRobinson: 26 August 2012 - 12:12 PM

Was This Post Helpful? 0
  • +
  • -

#3 CTphpnwb  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 3077
  • View blog
  • Posts: 10,790
  • Joined: 08-August 08

Posted 26 August 2012 - 01:44 PM

The idea behind removing old information from the table is to keep it from consuming resources. Even if a session would use fewer resources than the database its important to remember that a bot doesn't need to store the session cookie or it could store it, but delete it every time the bot fails to login. That's not to say this method is the ideal way either. To get around it a bot could be written to continually change proxy servers, so if you wanted to be extra secure you might use a hybrid of this and session variables.
Was This Post Helpful? 0
  • +
  • -

#4 dawmail333  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 27
  • View blog
  • Posts: 174
  • Joined: 02-July 07

Posted 26 August 2012 - 03:04 PM

View PostCTphpnwb, on 27 August 2012 - 06:44 AM, said:

The idea behind removing old information from the table is to keep it from consuming resources. Even if a session would use fewer resources than the database its important to remember that a bot doesn't need to store the session cookie — or it could store it, but delete it every time the bot fails to login. That's not to say this method is the ideal way either. To get around it a bot could be written to continually change proxy servers, so if you wanted to be extra secure you might use a hybrid of this and session variables.

If someone is going to make it change proxy servers, they're not going to bother storing session variables. IP based detection also is horrible to people behind a NAT (Starbucks). I would generally recommend you just have a login form, but make it wait two seconds before you actually check the password. This reduces the number of guesses they can make drastically, and is impossible to circumvent.

If you REALLY want to go down the IP based route, you might want to check the user agent as well.

P.S. Any query to a database can be quite expensive, another reason I don't like methods that store things.

This post has been edited by dawmail333: 26 August 2012 - 03:05 PM

Was This Post Helpful? 0
  • +
  • -

#5 CTphpnwb  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 3077
  • View blog
  • Posts: 10,790
  • Joined: 08-August 08

Posted 26 August 2012 - 04:09 PM

They don't have a choice when it comes to storing session variables, only the session id cookie. Even that must be stored to successfully login, so
they only have the option to delete it after an attempt fails.

As for IP addresses being a problem behind NAT, it's not likely that you'll have two or more legitimate users trying to log in at the same time from the same network. Even if they are, all they need to do is have one wait a few seconds!

I suppose the computational expense is relative. If you don't have bots trying to hack in, then why bother with this at all, but if you do, then the bulk of your processing time without something like this is going to be spent giving the hacker opportunities to break in.
Was This Post Helpful? 0
  • +
  • -

#6 JadenRP  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 2
  • Joined: 05-February 13

Posted 08 February 2013 - 02:33 PM

Wow thank you should I make a separate file or in include this in login.php
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1