Blocking Brute Force in PHP Website

PHP

Brute Force is a time consuming cracking process to log in to a user’s account of a web site. It’s not common and requires very high resources including the most important factor : TIME.

What’s Brute Force

Suppose, your email website have an account of username “abel”. His big enemy who is a nerd, wants to send rubbish stuff to Abel’s friends to make his friends angry. Your site is heavily protected except for Brute Force and the enemy is willing to give as much time to log in to Abel’s account.

Enemy makes a file with possible passwords and made a script that send login POST data to site’s login page. The script is continuously ran with passwords made in every possible way and finally after some hours or days, the enemy entered in to the user’s account.

A brute force attack makes millions of requests to your site which eventually makes your site down or heats up your server.

Solution

One of the many solutions is to block the user from doing login action for some time after 3 or 5 incorrect login attempts.

Another solution is to do a captcha verification every time for login action after 3 or 5 incorrect login attempts.

Another way is to temporarily blocking the user account after 3 or 5 incorrect login attempts and that user can only access his / her account after clicking a link that was sent to their email address.

Efficient Solution

There are more solutions which are superbly effective, but requires more usage of database. So, the most efficient way is to take as much less usage of database and at the same time, prevent brute forcing.

So, here’s what we’re going to do :

  • Create a new column in the users table for storing the current login status of the account
  • Update that column of user everytime the user attempts login whether it’s successful or not
  • Does action according to the value of the column

Let’s add the column with the name “attempt” with varchar(15) :

ALTER TABLE `users`  ADD `attempt` VARCHAR(15) NOT NULL ;

Now, on the code of login processing, we collect the current attempt value of the user :

$status = $rows['attempt'];

Before we continue, think of the $this->updateUser() function as a mechanism to update the user details according to the array as the first parameter of the function and the user is mentioned as it’s ID in second parameter.

Add this after the code of incorrect password :

if($status == ""){
    // User was not logged in before
    $this->updateUser(array(
       "attempt" => "1" // Tried 1 time
    ), $userID);
}else if($status == 5){
    $this->updateUser(array(
       "attempt" => "b-" . strtotime("+5 minutes", time()) // Blocked, only available for re-login at the time in UNIX timestamp
    ), $userID);
}else if(substr($status, 0, 2) == "b-"){
    // Account blocked
}else if($status < 5){     // If the attempts are less than 5 and not 5     $this->updateUser(array(
       "attempt" => $status + 1 // Tried current tries + 1 time
    ), $userID);
}

What the above code does is update the attempt field of the users table according to existing value in it :

  • if status is null, then update it with “1”
  • if status is less than “5”, then update it with adding “1” to the existing value
  • if status is “5”, then update it with “b-” plus the timestamp of current time + 5 minutes. eg : “b-1412580325”
  • if status is starting with character “b-“, do nothing

Add this before we check if password hash is that of the login password hash :

if(substr($status, 0, 2) == "b-"){
  $blockedTime = substr($status, 2);
  if(time() < $blockedTime){
     $block = true;
  }else{
     // remove the block, because the time limit is over
     $this->updateUser(array(
      "attempt" => "" // No tries at all
     ), $us_id);
  }
}

The above code will check if the account is blocked and if it is, then the variable $block will have value boolean TRUE. Also, if the time of blocking is over, then the attempt field is updated with a null value.

So, before when you check whether password is correct or not, see if the $block variable is set :

if(!isset($block)){
 // Check login
}

If it is set, then don’t process login and display a message to the user that the account is blocked. You can also retrieve the time when the blocking is over from $blockedTime variable. It is a UNIX timestamp value, so it can be changed to something like this :

echo date("Y-m-d H:i:s", $blockedTime); // 2014-10-20 12:00:00

This method for blocking brute force attack is used in the login system I created logSys.