Basic, Yet Effective PHP Throttle

Wed, 04/19/2017 - 10:13

Basic, Yet Effective PHP Throttle

Anyone who has spent any time looking at web server logs will know that most websites are bombarded with attempts to undermine the security features and take advantage of CMS and plugin vulnerabilities. On top of this there are many web-crawlers out there that simply don't follow any of the suggestions or rules and will continuously hit a page over and over again.

If the server, plugin, modules and code etc are up to date these attempts to hit the probe the server are generally just an annoyance. But you have to remember that each of these hits requires at the very least a hit to the server. More than likely however, depending on your website (that likely uses a Content Management System) this simple hit might also include multiple hits to images, javascript files, stylesheets, PHP and the database. A simple hit to the website over and over again might end up taking up server resources. 

For most Content Management Systems there are plugins that help protect the website, but they don't really mitigate the resource issue as they have to compare attack vectors against the database, which requires a webpage load, again taking up resources.

Method One: Stop It At The Server

There are a few tricks that can help stop some of the negative traffic before it even gets to PHP that I outline here. While affective these require a bit of, "gardening", if you will by regularly checking reports from security newsletters and server logs for patterns to manually keep ahead of what's going on.

Method Two: Something Dynamic

As long as the server is not dealing with a huge volume of traffic regularly, then this simple script should be able to alleviate some of the headache.

<?php
/** 
 * Super Basic Throttle
 * 
 * @author Jeremy Heminger http://jeremyheminger.com
 * @version 1.0.2
 * 
 * the idea here is that if placed before everything in the code this will stop a simple attack, 
 * malicious or poorly built web-crawler.
 * This script is mostly designed to reduce resource waste that comes from a full hit to the website and database.
 * This is not designed with a DDOS in mind, but might provide SOME relief in that case.
 * 
 * @param int $max the maximum hits before a throttle will occur
 * 
 * @param string $rtime the release time ( use strtotime ) http://php.net/manual/en/function.strtotime.php
 * 
 * @param string $message an error message if the threshold is met (defaults to '' because ... f*ck the haters)
 * 
 * @param string $file path to a file
 * 
 * @return void
 * */
function throttle($max = 5, $rtime = '-5 minutes', $message = '', $file = '.ips')
{
    // get IP address 
	$ip = $_SERVER['REMOTE_ADDR'];
	if(empty($ip))die(); // if no IP then something is wrong...no message for you
	// get previously stored IP addresses
	if(file_exists($file)) {
		$ips = file_get_contents($file);
		$ips = json_decode($ips);
	}else{
		// no data, then create empty object
		$ips = new stdClass();
	}
	// if ip is alreay in system
	if(isset($ips->$ip->count)) {
		// if its been $rtime
		if($ips->$ip->ts < strtotime($rtime)) {
			// delete
			unset($ips->$ip);	
		}else{
			// otherwise if its over $max hits
			if($ips->$ip->count >= $max) {
				// die with message
                                header("HTTP/1.0 429 Too Many Requests");
				die($message);	
			}else{
				// else increment
				$ips->$ip->count++;
				$ips->$ip->ts = strtotime('now');
			}
		}
	}else{
		// set first hit
		$ips->$ip->count = 1;
		$ips->$ip->ts = strtotime('now');
	}
	// record what happened
	file_put_contents($file,json_encode($ips));
}
?>

Forkit on GitHub

Category