IP flood detection, DDoS detector for PHP

apps

#1

Now, I didn’t come up with the original script for this, someone else did (on webmaster world?) the “read and write to file” parts. It will take the IP address and create an MD5 hash of it and use that to track the visiting times per ip, it is possible that two visitors on the same IP or proxy will trip the flood filter - but I say, though titties.

You will get an email each time a user is banned (mainly for testing and fun), the defaults will ban someone for 60 seconds if they do more than 9 hits in 10 seconds.

There are two parts to this. A php script, which you can either insert into your index.php, or a common functions file, or as an include_once(‘filename.php’);
And a 777 writeable directory to store the tracked IPs with a iplog.dat file in there. This script will eventually create 255, 2 digit, 0kb files in the folder to track each ip.

The php. I created a floodstopper.php file and in any script like index.php for my forum or site I put include_once(‘floodstopper’); near the start and it will execute this script every time someone hits the index.php file. If you want to be really nasty you could make a .htaccess file which redirects all 404 file not found errors to this script as well - just in case. or maybe redirect all image leechers to this etc.

[code]<?php
$cookie = $_COOKIE[‘yourcookie’]; /* if you have a known cookie on your site,
you can use this, otherwise just ignore this, it will set a different limit
for people with this cookie */

/* I use yourothercookie as the cookie ID for the forum, my forum uses ID
greater than 0 for all members and -1 for guests and members who have logged out,
so making it match greater than zero means members will get better access and
guests with or without cookies won’t */
$othercookie = $_COOKIE[‘yourothercookie’];
if($cookie && $othercookie > 0) $itime = 20; // Minimum number of seconds between visits
else $itime = 10; // Minimum number of seconds between visits
$ipenalty = 60; // Seconds before visitor is allowed back
if($cookie && $othercookie > 0)$imaxvisit = 30; // Maximum visits per $iteme segment
else $imaxvisit = 9; // Maximum visits per $iteme segment
$iplogdir = “./iplog/”;
$iplogfile = “iplog.dat”;

$ipfile = substr(md5($_SERVER[“REMOTE_ADDR”]), -2);
$oldtime = 0;
if (file_exists($iplogdir.$ipfile)) $oldtime = filemtime($iplogdir.$ipfile);

$time = time();
if ($oldtime < $time) $oldtime = $time;
$newtime = $oldtime $itime;

if ($newtime >= $time $itime*$imaxvisit)
{
touch($iplogdir.$ipfile, $time $itime*($imaxvisit-1) $ipenalty);
$oldref = $_SERVER[‘HTTP_REFERER’];
header(“HTTP/1.0 503 Service Temporarily Unavailable”);
header(“Connection: close”);
header(“Content-Type: text/html”);
echo “


Too many page views (more than “.$imaxvisit.” visits within “.$itime.” seconds)
by your IP address. Unregistered visitor hackers get less privileges.

”;
echo “Please wait “.$ipenalty.” seconds and try again.


Or go to google.com “;
$fp = fopen($iplogdir.$iplogfile, “a”);
if ($fp)
{
$useragent = “”;
if (isset($_SERVER[“HTTP_USER_AGENT”])) $useragent = $_SERVER[“HTTP_USER_AGENT”];
fputs($fp, $_SERVER[“REMOTE_ADDR”].” “.date(“d/m/Y H:i:s”).” “.$useragent.”\n”);
fclose($fp);
$yourdomain = $_SERVER[‘HTTP_HOST’];

//the @ symbol before @mail means ‘supress errors’ so you wont see errors on the page if email fails.
if($_SESSION[‘reportedflood’] < 1 && ($newtime < $time $itime $itime*$imaxvisit))
@mail(‘webmaster@’.$yourdomain, $yourdomain.'site flood by ‘.$cookie.’ '
.$_SERVER[‘REMOTE_ADDR’],'flood occured and ban for ‘.$_SERVER[‘REMOTE_ADDR’].’ at ‘
.$_SERVER[‘REQUEST_URI’].’ from ‘.$oldref.’ agent ‘.$_SERVER[‘HTTP_USER_AGENT’].’ ‘
.$cookie.’ '.$othercookie);
$_SESSION[‘reportedflood’] = 1;
}
exit();
}
else $_SESSION[‘reportedflood’] = 0;

touch($iplogdir.$ipfile, $newtime);
?>[/code]and create a directory called /iplog/ and make it writeable CHOWN 777 (log into webFTP from control panel to do this) (or 755 if that works on your server, but I doubt it!).
create a file in /iplog/ called iplog.dat and CHOWN it 666 (log into webFTP from control panel to do this).

Hope it works, if not I can help as I have about 3 copies all running in different places. I will post another php / htaccess combination here which you could use in unison with this script to automatically permanently ban visitors to are too frequent for your tastes.


#2

What constitutes a “hit?” Are you basing it off web log type hits. If that’s the case, then you got a problem. A single web page, like this forum, can produce more than a dozen “hits” to the server before the page is fully created.

Every .php page is a hit. Every image is a hit. Ever CSS file, ever javascript file, every reference to any other files on your server through the HTTP protocol is concidered a “hit.”

Hope you took that into account when you set your limits.


yerba# rm -rf /etc
yerba#


#3

Yeah I meant per-page. Not hits, each time the script is executed.


#4

Thanks for posting the code tez.

As I said in a previous post, PHP still looks rather ‘foreign’ to me, but I have found that examining others code to be one of the best ways to get a feel for a new language.

Thanks again.

Mark


Save [color=#CC0000]$50[/color] on DreamHost hosting using promo code [color=#CC0000]SAVEMONEY[/color] ( Click for promo code details )


#5

Hey tez,

Nice idea. I thought about time-based denial a while back, along with various other methods. Just never got round to writing anything.

Having been hit with psycheclone this past week on a few sites, I’ll give this a whirl and see how it goes. If I make any useful tweaks or stuff I’ll post 'em back.

Oh, one thing. The code on lines 25, 27, 29 and 51 has been mangled: some of the operands are missing. I guessed that the one on line 25 was a ‘plus’ but I’d be taking a wild stab in the dark at the ones on line 27 / 29 / 51. I have an idea of what makes sense, but it’d be better if you edited the post to include the missing characters or provided a downloadable version.

Thanks in advance.


#6

The plus symbols are missing!! This damn forum does not let you edit old posts and can not enter a plus sign!!! Anyone can do better than that. tip: use HTML encoded characters like this +

25 is missing a plus
$newtime = $oldtime + $itime;

27 is missing a plus in the middle. should be
($newtime >= $time + $itime*$imaxvisit)

29 missing pluses
touch($iplogdir.$ipfile, $time + $itime*($imaxvisit-1) + $ipenalty);

51 missing pluses
if($_SESSION[‘reportedflood’] < 1 && ($newtime < $time + $itime + $itime*$imaxvisit))


#7

I created a flood detection with Memcached. It uses the remote address to identify the client. 100 request per minute is maximum allowed in this example, if exceeded block for 360sec. Amend variables $limit, $seconds, $block_for_seconds.

[code]<?php

// check for spammer, refuse any loads if exceeds limit
// try to block if server is flooded

$limit = 100; // $limit requests per $seconds are allowed
$seconds = 60;
$block_for_seconds = 360;

$status = ‘OK’;

$memcache = new Memcache;
$memcache->connect(‘localhost’, 11211);

$ip = $_SERVER[‘REMOTE_ADDR’];

$r = $memcache->get($ip, array(‘c’, ‘t’));

$c = 1; // count
$init_time = time();
if($r) {
$s = $r[3]; // status
$c = $r[0]+1;
$init_time = $r[1];
if($s == ‘TOO_MANY_REQUESTS’) {
$d = time()-$r[1]; // time since block
if($block_for_seconds-$d > 0) { // still blocked
die(‘Flood detected. Please wait ‘.($block_for_seconds-$d).’ and try again.’);
} else { // block is over
$status = ‘OK’;
$init_time = time();
$c = 0;
}
}

$new_time = time();
if($c > $limit) { // check if happened within a minute
$time_elapsed = $new_time - $init_time;
if($time_elapsed < $seconds) { // to many request per sec -> block
$status = ‘TOO_MANY_REQUESTS’;
}
print “time elapsed: $time_elapsed, count:$c”;
$c = 0;
$init_time = time();
}
}
print_r($r);
$memcache->set($ip, array($c, $init_time, $new_time, $status) );

[/code]