sauviq
Last Updated: May 17, 2019
·
8.702K
· jasny

Brute force protection in PHP

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. This is a hurdle that should keep at the casual hackers and script kiddies out. 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 user cache.

<?php
  $apc_key = "{$_SERVER['SERVER_NAME']}~login:{$_SERVER['REMOTE_ADDR']}";
  $tries = (int)apc_fetch($apc_key);
  if ($tries >= 10) {
    header("HTTP/1.1 429 Too Many Requests");
    echo "You've exceeded the number of login attempts. We've blocked IP address {$_SERVER['REMOTE_ADDR']} for a few minutes.";
    exit();
  }

  $success = login($_POST['username'], $_POST['password']);
  if (!$success) {
    apcu_inc($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.

<?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) {
    header("HTTP/1.1 429 Too Many Requests");
    echo "You've exceeded the number of login attempts. We've blocked IP address {$_SERVER['REMOTE_ADDR']} for a few minutes.";
    exit();
  }

  $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);
  }

4 Responses
Add your response

29304

The above example is note working for me.
When i am print the $tries variable , it always showing 0.

over 1 year ago ·
30261

I find this solution very usable especially because it doesn't need database connection. I am rather new into php cache but isn't something similar possible to do using opcache so there will be no need to install required additional APCu cache module into php? Thanks.

10 months ago ·
31456

Hey I love this article but there is a mistake in the first example.

apcuinc($apckey, $tries+1, 600);

should be

apcustore($apckey, $tries+1, 600);

I think

5 months ago ·
33011

I am delighted with the site . Thank you very much. I'm looking for developers for the site https://nordspil.com . If someone is interested, please write me a private message.

4 days ago ·