9 Replies - 6804 Views - Last Post: 25 December 2014 - 05:09 PM

#1 CTphpnwb  Icon User is online

  • D.I.C Lover
  • member icon

Reputation: 3695
  • View blog
  • Posts: 13,355
  • Joined: 08-August 08

Another attempt at defending brute force attacks

Post icon  Posted 10 September 2014 - 08:45 AM

*
POPULAR

Atli has an excellent tutorial on using a queue for this purpose, but I think I may have stumbled on a simpler method. I think that even if a hacker knew of this algorithm and wrote their script to circumvent it the delay would make a brute force attack impractical. I'd like some feedback/suggestions.

The idea is that since we only really know the user id and password provided we should keep track of them and count the number of attempts over a given time period. If there are more than one attempts with a different password then assume it's a hacker and respond as if the password is wrong, but without actually testing it. If there are two attempts with the same password, assume that it's from a person and test the password for the user. Below is the queue method from my user class and the table I'm using in my testing. Both need refinement, but seem to work.

Note: If/when I'm sure that this works well I'll probably use a stored procedure so I can avoid so many round trips to the database.
	public function queue_ok() {
		$seconds = array(5);
		$myhash = hash('snefru256', "salt_for_logins".$this->userpass, false);
		$userinfo = array($this->username, $this->uid, $myhash);

		$query_del = "DELETE FROM LoginAttempts WHERE CURRENT_TIMESTAMP() - attempttime > ?";
		$query_add = "INSERT INTO LoginAttempts (username, uid, attempttime, PassHash) VALUES (?, ?, CURRENT_TIMESTAMP(), ?)";
		$query_count_same = "SELECT COUNT(*) as same_attempts FROM LoginAttempts WHERE ((`username` = ? OR `uid` = ? ) AND `PassHash` = ?)";
		$query_count = "SELECT COUNT(*) as num_attempts FROM LoginAttempts WHERE `username` = ? OR `uid` = ? ";

		// First, remove old attempts from the table
		$del = $this->db->prepare($query_del);
		$del->execute($seconds);
		
		// Next, add this new attempt to the table.
		$add = $this->db->prepare($query_add);
		$add->execute($userinfo);
		
		// Now get the current number of recent (less than 5 seconds) login attempts for this user that use the current password.
		// This is so we can allow users to login even if a script is attempting to hack their account at the same time.
		$count_same = $this->db->prepare($query_count_same);
		$count_same->setFetchMode(PDO::FETCH_ASSOC);
		$count_same->execute($userinfo);
		$result_same = $count_same->fetch();

		unset($userinfo[2]);				 
		// Now get the current number of recent (less than 5 seconds) login attempts for this user
		$count = $this->db->prepare($query_count);
		$count->setFetchMode(PDO::FETCH_ASSOC);
		$count->execute($userinfo);
		$result = $count->fetch();
		
		// If the number of attempts is 1, then return true so that the login method can test this password against this user's stored password.		
		if($result['num_attempts'] == 1) return true;
		// If the number of attempts with this password for this user in the past X (currently 5) seconds is 2, return true so that the login method can test this password against this user's stored password.
		// This means that if hacker is attempting to brute force a user's password and that user attempts to login they will be rejected on the first attempt, but if they try again withing 5 seconds their password will work. 	
		if($result_same['same_attempts'] == 2) return true;
		
		// If neither of the above is true, return false so that the login method will not validate the password, it will simply act as if the password did not validate, giving the hacker no clues as to why.
		return false;
	}


Table
CREATE TABLE `LoginAttempts` (
  `username` varchar(255) NOT NULL,
  `uid` varchar(55) NOT NULL,
  `attempttime` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  `PassHash` char(64) NOT NULL,
  KEY `attempttime` (`attempttime`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;



Is This A Good Question/Topic? 5
  • +

Replies To: Another attempt at defending brute force attacks

#2 macosxnerd101  Icon User is offline

  • Games, Graphs, and Auctions
  • member icon




Reputation: 12134
  • View blog
  • Posts: 45,114
  • Joined: 27-December 08

Re: Another attempt at defending brute force attacks

Posted 10 September 2014 - 08:47 AM

Awesome question! I'm going to move this to the Advanced Discussion forum and feature it. :)
Was This Post Helpful? 0
  • +
  • -

#3 CTphpnwb  Icon User is online

  • D.I.C Lover
  • member icon

Reputation: 3695
  • View blog
  • Posts: 13,355
  • Joined: 08-August 08

Re: Another attempt at defending brute force attacks

Posted 10 September 2014 - 11:10 AM

Ok, I found a flaw. If a hacker knew about this defense they might write their script to send the same user information twice in a row, barely slowing them down. My fix is below:
	public function queue_ok() {
		$seconds = array(5);
		$myhash = hash('snefru256', "salt_for_logins".$this->userpass, false);
		$userinfo = array($this->username, $this->uid, $myhash);

		$query_del = "DELETE FROM LoginAttempts WHERE CURRENT_TIMESTAMP() - attempttime > ?";
		$query_count = "SELECT COUNT(*) as num_attempts FROM LoginAttempts WHERE `username` = ? OR `uid` = ? ";
		$query_count_same = "SELECT attempttime FROM LoginAttempts WHERE ((`username` = ? OR `uid` = ? ) AND `PassHash` = ?) ORDER BY attempttime ASC";
		$query_add = "INSERT INTO LoginAttempts (username, uid, attempttime, PassHash) VALUES (?, ?, CURRENT_TIMESTAMP(), ?)";

		// First, remove old attempts from the table
		$del = $this->db->prepare($query_del);
		$del->execute($seconds);
		
		// Next, add this new attempt to the table.
		$add = $this->db->prepare($query_add);
		$add->execute($userinfo);
		
		// Now get the current number of recent (less than 5 seconds, but greater than 1 second apart) login attempts for this user that use the current password.
		// This is so we can allow users to login even if a script is attempting to hack their account at the same time.
		$same = $this->db->prepare($query_count_same);
		$same->setFetchMode(PDO::FETCH_ASSOC);
		$same->execute($userinfo);
		$rows = $same->fetchAll();
		if(count($rows) == 2) {
			$attempt_time = array(0,0);
			$i = 0;
			foreach($rows as $row) {
				$attempt_time[$i] = strtotime($row['attempttime']);
				$i++;
			}
			//error_log($attempt_time[1]." - ".$attempt_time[0]." = ".($attempt_time[1]-$attempt_time[0]));
			if($attempt_time[1] - $attempt_time[0] > 1) return true;
		}
		// This means that if hacker is attempting to brute force a user's password and that user attempts to login they will be rejected on the first attempt, but if they try again withing 5 seconds (and after 1 second) their password will work.

		unset($userinfo[2]);
		
		// Now get the current number of recent (less than 5 seconds) login attempts for this user
		$count = $this->db->prepare($query_count);
		$count->setFetchMode(PDO::FETCH_ASSOC);
		$count->execute($userinfo);
		$result = $count->fetch();

		// If the number of attempts is 1, then return true so that the login method can test this password against this user's stored password.
		if($result['num_attempts'] == 1) return true;
		
		return false;
	}

This post has been edited by CTphpnwb: 10 September 2014 - 11:42 AM

Was This Post Helpful? 0
  • +
  • -

#4 CasiOo  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1567
  • View blog
  • Posts: 3,520
  • Joined: 05-April 11

Re: Another attempt at defending brute force attacks

Posted 10 September 2014 - 11:36 AM

I would much rather have a solution where you aren't relaying on a low-timed delay
How about blocking the ip from login after 5-10 failed attempts
Send a confirmation SMS or e-mail to the user with a link to unblock the ip
The e-mail could contain a reset link, which most users at this point will need anyway ^^
You would be blocking on ip so you won't annoy the crap out of the account's real owner

If there's an absurd amount of login attempts with different ips, then send a confirmation SMS/e-mail no matter what
It would even be weird if two different ips were used in a small time span!
Was This Post Helpful? 0
  • +
  • -

#5 CTphpnwb  Icon User is online

  • D.I.C Lover
  • member icon

Reputation: 3695
  • View blog
  • Posts: 13,355
  • Joined: 08-August 08

Re: Another attempt at defending brute force attacks

Posted 10 September 2014 - 11:48 AM

The problem with that is it's easy to lock a user - or many users - out of their accounts. Even though it's only temporary, if a hacker's script did it repeatedly over a period of days they would have an effective DOS attack. I'm trying to block the hacker with minimal inconvenience to the user, and in a way that gives the hacker little to no information. With this solution, the only glitch that would be noticed would be that the legitimate user is rejected on their first attempt (when an attack is in progress) but then would be logged in on their second. Hopefully, it's just a matter of adjusting the time limits.
Was This Post Helpful? 0
  • +
  • -

#6 CasiOo  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1567
  • View blog
  • Posts: 3,520
  • Joined: 05-April 11

Re: Another attempt at defending brute force attacks

Posted 10 September 2014 - 12:26 PM

Yes locking people out of their accounts is a potential problem

Other ways of doing authentication should also be considered
I really like two step authentication. It seems to be a good way of adding an extra layer of security to your login
Was This Post Helpful? 0
  • +
  • -

#7 ge∅  Icon User is offline

  • D.I.C Lover

Reputation: 175
  • View blog
  • Posts: 1,116
  • Joined: 21-November 13

Re: Another attempt at defending brute force attacks

Posted 10 September 2014 - 01:37 PM

I don't like the idea of rejecting a good password. I think it's a smart and fresh idea, but I don't like it.

What if I typed a wrong password the first time, then got it right the second time ? I'll have to try 3 times before I can finally log in. And there is no guaranty I'll attempt to enter the same login the 3rd time: I'm more likely to think that I've confused my password with another one or that I've forgotten it. In such case it can take a while before I try the correct password again.

What if my browser fills the password field automatically ? If it has always worked, will I blame the browser or the site ? If the site doesn't appear to be working properly my trust will probably decrease.

This post has been edited by ge∅: 10 September 2014 - 01:39 PM

Was This Post Helpful? 1
  • +
  • -

#8 CTphpnwb  Icon User is online

  • D.I.C Lover
  • member icon

Reputation: 3695
  • View blog
  • Posts: 13,355
  • Joined: 08-August 08

Re: Another attempt at defending brute force attacks

Posted 10 September 2014 - 02:31 PM

View PostCasiOo, on 10 September 2014 - 03:26 PM, said:

I really like two step authentication. It seems to be a good way of adding an extra layer of security to your login

That adds inconvenience to every login attempt.

View Postge∅, on 10 September 2014 - 04:37 PM, said:

I don't like the idea of rejecting a good password. I think it's a smart and fresh idea, but I don't like it.

I wonder how many others feel this way. I suppose I could add a catcha whenever there's a recent attempt to login with the same user name already in the table.
Was This Post Helpful? 0
  • +
  • -

#9 creativecoding  Icon User is offline

  • Hash != Encryption
  • member icon


Reputation: 931
  • View blog
  • Posts: 3,216
  • Joined: 19-January 10

Re: Another attempt at defending brute force attacks

Posted 23 November 2014 - 06:03 PM

I like this solution mainly because it does what hash-based security seems to be all about these days: increasing the amount of time required to brute-force crack a password. Even if they circumvent your original solution by sending the password twice, that is still doubling the time required to crack the account.

You can extend this script by tracking the login IPs, user agent, and other variables provided by the user who owns the account. If you have 90% successful logins from one IP, and they try again - it's safe to assume it's not an attacker.
Was This Post Helpful? 0
  • +
  • -

#10 guyfromri  Icon User is offline

  • D.I.C Addict

Reputation: 46
  • View blog
  • Posts: 836
  • Joined: 16-September 09

Re: Another attempt at defending brute force attacks

Posted 25 December 2014 - 05:09 PM

So I have thought about this and there is one method I really like that is used by my banking app and a lot of corporations.

There is a lot of useful data coming from the user but what it you quantified the login data a little? Maybe when the user signs up, make them pick an image from a list of 50. (Something silly like a smiley face or a palm tree.) Then when logging in, the user selects the image from 6 potential in random order, enters name and enters password. When the user changes their password, suggest that they select a new image as well.

In my mind, if the user has a password that doesn't match the existing password(first 5 characters, last 5, first 1, whatever data may be close if it was a typo) and picks the wrong image 3 times as well, it is much more likely a hacking attempt.

It can work in other ways too such as a pin that corresponds with your password but by adding an additional variable, you're going to really increase the security from this type of attack and not lock out potentially good users who may have updated their password and made a simple mistake. On the flip side of the coin, these methods could seem overly tasking for your users depending on your demographic. Just my thoughts.

This post has been edited by guyfromri: 25 December 2014 - 05:11 PM

Was This Post Helpful? 0
  • +
  • -

Page 1 of 1