If you have a system where users can subscribe and choose there own passwords, you are probably a target for brute force attacks like a dictionary attack. You can limit this problem by showing user how strong their password is. However forcing users to enter a really strong password will annoy them, since they like something they can remember.

Another way wall you can put up is blocking an IP address for a few minutes upon a number of login failures. This is not a waterproof protection, but the hacker now requires a botnet to perform the brute force attack. To rent a botnet is quite expensive and to create one is quite difficult, so your average script kiddie doesn’t have one of those. So based on the data you’re protecting this should be a decent defense.

Setting up this defense isn’t difficult. I’ll show an example how to do this with APC.

1
2
3
4
5
6
7
8
9
<?php
  $apc_key = "{$_SERVER['SERVER_NAME']}~login:{$_SERVER['REMOTE_ADDR']}";
  $tries = (int)apc_fetch($apc_key);
  if ($tries >= 10) throw new Exception("You've exceeded the number of login attempts. We've blocked IP address {$_SERVER['REMOTE_ADDR']} for a few minutes.");
 
  $success = login($_POST['username'], $_POST['password']);
  if (!$success) apc_store($apc_key, $tries+1, 600);  # store tries for 10 minutes
   else apc_delete($apc_key);
?>
<?php
  $apc_key = "{$_SERVER['SERVER_NAME']}~login:{$_SERVER['REMOTE_ADDR']}";
  $tries = (int)apc_fetch($apc_key);
  if ($tries >= 10) throw new Exception("You've exceeded the number of login attempts. We've blocked IP address {$_SERVER['REMOTE_ADDR']} for a few minutes.");

  $success = login($_POST['username'], $_POST['password']);
  if (!$success) apc_store($apc_key, $tries+1, 600);  # store tries for 10 minutes
   else apc_delete($apc_key);
?>

Increasing the block time
Blocking an IP might effect a whole office which can be annoying. Starting with a low timeout and increasing it each time an IP is blocked will help against it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
  $apc_key = "{$_SERVER['SERVER_NAME']}~login:{$_SERVER['REMOTE_ADDR']}";
  $apc_blocked_key = "{$_SERVER['SERVER_NAME']}~login-blocked:{$_SERVER['REMOTE_ADDR']}";
 
  $tries = (int)apc_fetch($apc_key);
  if ($tries >= 10) throw new Exception("You've exceeded the number of login attempts. We've blocked IP address {$_SERVER['REMOTE_ADDR']} for a few minutes.");
 
  $success = login($_POST['username'], $_POST['password']);
  if (!$success) {
    $blocked = (int)apc_fetch($apc_blocked_key);
 
    apc_store($apc_key, $tries+1, pow(2, $blocked+1)*60);  # store tries for 2^(x+1) minutes: 2, 4, 8, 16, ...
    apc_store($apc_blocked_key, $blocked+1, 86400);  # store number of times blocked for 24 hours
  } else {
    apc_delete($apc_key);
    apc_delete($apc_blocked_key);
  }
?>
<?php
  $apc_key = "{$_SERVER['SERVER_NAME']}~login:{$_SERVER['REMOTE_ADDR']}";
  $apc_blocked_key = "{$_SERVER['SERVER_NAME']}~login-blocked:{$_SERVER['REMOTE_ADDR']}";

  $tries = (int)apc_fetch($apc_key);
  if ($tries >= 10) throw new Exception("You've exceeded the number of login attempts. We've blocked IP address {$_SERVER['REMOTE_ADDR']} for a few minutes.");

  $success = login($_POST['username'], $_POST['password']);
  if (!$success) {
    $blocked = (int)apc_fetch($apc_blocked_key);

    apc_store($apc_key, $tries+1, pow(2, $blocked+1)*60);  # store tries for 2^(x+1) minutes: 2, 4, 8, 16, ...
    apc_store($apc_blocked_key, $blocked+1, 86400);  # store number of times blocked for 24 hours
  } else {
    apc_delete($apc_key);
    apc_delete($apc_blocked_key);
  }
?>

  14 Responses to “Simple brute force protection with APC”

  1. My friend and ex-employer Dirk Bonenkamp, came with a slight improvement for this scheme. Blocking an IP might effect a whole office which can be annoying. Starting with a low timeout and increasing it each time an IP is blocked will help against it.

    I’ve changed to article to include this solution.

    ReplyReply
  2. Would it be an idea to clear the blocked count when a login has been successfull?

    ReplyReply
  3. Yes I have that in my own code, but I failed to put it in this sniplet.

    ReplyReply
  4. You’ll want to be careful with doing this thing just on IP basis, because once you get sufficient loading you’ll essentially perma-block AOL customers (as one example ISP) because they all pretty much show up as a very small set of IPs. More information here: http://webmaster.info.aol.com/proxyinfo.html

    The net result is a moderate-to-high load implementation of this needs to support varying threshold levels on a per IP basis. Wikipedia has a good list of widely used ISP proxy IPs as well. They’ve managed to get most folks to set the XFF-Forwarded-For header but that’s still a bit dicey to rely on.

    Another idea that I’ve seen used with some success is simply slowing down response times for IPs which have high failure rates, instead of outright blocking them. (You can cut their throughput by 3-4x without significant user experience issues.)

    Andrew

    ReplyReply
  5. oh, a good idea . use APC as a cache

    ReplyReply

 Leave a Reply

(required)

(required)

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code lang=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" extra="">

   

Questions? Just ask!

About the author

Hi, I'm Arnold Daniels. How nice that you like to know a bit more about little old me :).

I've spend a big part of my life behind a computer (and not playing games). I've learned a lot about databases, programming and system administration especially on. the LAMP stack (Linux, Apache, MySQL & PHP).

Have a look at what I'm working on now!
© 2012 Jasny · web development Suffusion theme by Sayontan Sinha