In this post we are going to describe how you can stop brute force attacks by using a simple throttling script in PHP. The class is self-descriptive. But still just to understand better we will describe what it doesn and how you can achieve the same.
What is Brute Force:
Suppose you have a login service on your website which can be used by users to login. But hackers will try to log into accounts of other users to gain personal information. Or the hackes might try to gain access to administrator panel and thereby compromising all sensitive data. For this hackers generally write scripts which execute automatically and try to enter a different username and password combination.
For some admin panels users always choose username as admin or administrator (which is already a bad practice). So 1 our of 2 things is already available to the attacker/spammer. Now he just need to try different passwords combination in order to gain access.
Brute Force also makes serving speed slow:
As the attacker is making 100s of requests per second this also impacts your overall web server speed. So your database might get locked or can get freezed due to so many simultaneous requests.
What is Throttling:
Throttling is a technique where you limit the usage of resource depending on few conditions. For example, if you have ever visited a banking website and tried to login, you will be blocked to attempt login after you have made 3 simultaneous wrong login attempts. This is done in order to stop brute force. The conditions of throttling may vary depending on the application’s requirement. To learn more about throttling follow here ->
Benefits of Throttling:
Throttling saves your web server’s precious resorces like bandwidth, memory and cpu usage.
It helps you safegaurd your application from potential hackers and spammers. To learn more about php click here ->
Usage of script below:
Below script usage will allow you to stop brute force attacks on any of you resources.
1 2 3 4 5 6 7 8 | try { //this limits the number of times a user can access the login page to 5 every 2 minutes. SecurityUtil::throttleResource('/admin/login-page', 5, 120); //if code gets to here, no exception thrown, which means user has not exceeded allowed number of attempts, so process the login (or whatever request) } catch (Exception $e) { echo "Number of allowed login attempts exceeded. Please try again later."; exit; } |
Full Code Below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | <?php class SecurityUtil { /** * $resource - a string representing the page name or URL or any other key you want to use to throttle requests for. * $rate - an int specifying the maximum number of allowed access requests per window period * $window - number of seconds representing your throttle window * $any_user - if false, the $_SERVER['REMOTE_ADDR'] IP of the client making the request is used to throttle number of requests * only for that client. If set to true, then the resource is throttled globally against requests from ANY user IP (good for DDoS prevention). * * returns nothing if the user should not be throttled, throws an Exception if user has exceeded allowed number of requests. * * NOTE: The constant THROTTLE_FILE needs to be defined, which should point to the full path on the server to a text file that is writable by Apache. * Usage: just call this function whenever you process a request that you want to throttle, passing in the request-url (or some other identifying key), * and the throttle parameters. */ //Path to your cache file which will be created to store history of throttle in encoded form const THROTTLE_FILE = "./throttleCache.txt"; public static function throttleResource($resource, $rate = 7, $window = 60, $any_user = false) { //load the cache from file $cache = file_exists(self::THROTTLE_FILE) ? self::varFromFile(self::THROTTLE_FILE) : array(); //load php array from file $user = $any_user ? '0.0.0.0' : $_SERVER['REMOTE_ADDR']; //initialize cache variables if (!isset($cache[$user])) $cache[$user] = array(); if (!isset($cache[$user][$resource])) $cache[$user][$resource] = array(); //timestamp the cache resource access $cache[$user][$resource][] = time(); //only hold the required number of timestamps if (count($cache[$user][$resource]) > $rate) array_shift($cache[$user][$resource]); //check if resource has exceeded allowed access requests $deny_access = false; $attempts = $cache[$user][$resource]; if (count($attempts) < $rate) $deny_access = false; //didn't exceed allowed access requests elseif ($attempts[0] + $window > time()) $deny_access = true; //else matched or exceeded allowed requests, and oldest access was within the window else $deny_access = false; //otherwise oldest access was not within window, so allow //cleanup the cache so it doesn't get too large over time foreach ($cache as $ip => $resources) { //for each IP address record foreach ($resources as $res => $attempts) { //for each IP resource bucket if ($attempts) { //if resource access record exist if ($attempts[count($attempts) - 1] + $window < time()) { //if latest record is older than window unset($cache[$ip][$res]); //irrelevant, delete the record } } } if (!$resources) unset($cache[$ip]); //if the IP record has no resource buckets, delete it } //store the cache back to file self::varToFile(self::THROTTLE_FILE, $cache); if ($deny_access) throw new Exception('Maximum access requests exceeded.'); } public static function varToFile($filename, $var) { $data = gzcompress(serialize($var), 9); return self::stringToFile($filename, $data); } public static function varFromFile($filename) { if (file_exists($filename)) { $data = unserialize(gzuncompress(file_get_contents($filename))); if ($data) return $data; else return false; } else return false; } public static function fileToString($filename) { return file_get_contents($filename); } public static function stringToFile($filename, $data) { $file = fopen($filename, "w"); fwrite($file, $data); fclose($file); return true; } } ?> |